P5610 [Ynoi2013] 大学

谁说平衡树过不了。

思路

思路就简单说了,主要讲一下卡常技巧。

5×1055 \times 10^5 范围内的数约数最多有 200200 个约数。于是考虑用平衡树维护。

aia_i 所有约数所对应的平衡树中插入一个 ii。然后在 ii 这个平衡树中,将 [l,r][l,r] 区间 split 出来,就得到了我们需要的这个子树,同时筛掉 kaik \nmid a_i 的元素。

考虑用树状数组统计答案。直接在 DFS 平衡树的时候,修改答案即可。

接下来是一些卡常技巧。

  1. 处理约数不要用 Θ(nn)\Theta(n\sqrt{n}) 的算法,用埃筛的方式 Θ(nlnn)\Theta(n \ln n) 的算法代替。

  2. 因为我们需要保证建树的时候传进去的数值是有序的,否则建树的时候就需要 merge 复杂度是 Θ(nlogn)\Theta(n \log n) 的。所以我们将每一个数的约数都加进一个 vector 里面,然后按照 1n1 \sim n 的顺序将 ii 加入到 aia_i 所对应的平衡树里面。同时埃筛的时候只需要处理在数组的元素。

  3. 平衡树不要废节点回收,空间没什么优化,时间会慢很多。

  4. 平衡树不要把随机的权值丢到结构体里面,在 merge 函数里面随机即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <bits/stdc++.h>
#define re register
#define ll long long

using namespace std;

mt19937 rnd(random_device{}());
const int N = 1e5 + 10,M = 5e5 + 10,K = 3e7 + 10;
int n,q,rt[M]; ll lst;
int num,arr[N],tmp[N];
vector<int> p[M],v[M];
bool vis[M];

inline int read(){
int r = 0;
char c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r;
}

struct BIT{
#define lowbit(x) (x & -x)

ll tr[N];

inline void modify(int x,int k){
for (re int i = x;i <= n;i += lowbit(i)) tr[i] += k;
}

inline ll query(int x){
ll res = 0;
for (re int i = x;i;i -= lowbit(i)) res += tr[i];
return res;
}

#undef lowbit
}BT;

struct FHQ{
#define ls(u) (tr[u].l)
#define rs(u) (tr[u].r)

int idx;

struct node{
int l,r,val;
}tr[K];

inline int newnode(int k){
tr[++idx] = {0,0,k};
return idx;
}

inline void split(int u,int k,int &x,int &y){
if (!u) return x = y = 0,void();
if (tr[u].val <= k){
x = u; split(rs(u),k,rs(u),y);
}
else{
y = u; split(ls(u),k,x,ls(y));
}
}

inline int merge(int x,int y){
if (!x || !y) return x | y;
if (rnd() < rnd()) return rs(x) = merge(rs(x),y),x;
else return ls(y) = merge(x,ls(y)),y;
}

inline int build(int l,int r){
if (l > r) return 0;
int mid = l + r >> 1;
int u = newnode(tmp[mid]);
tr[u].l = build(l,mid - 1); tr[u].r = build(mid + 1,r);
return u;
}

inline void dfs(int u,int k){
if (ls(u)) dfs(ls(u),k);
if (u == ls(u) || u == rs(u)) exit(0);
if (arr[tr[u].val] % k == 0){
BT.modify(tr[u].val,-arr[tr[u].val]);
BT.modify(tr[u].val,arr[tr[u].val] /= k);
if (arr[tr[u].val] % k == 0) tmp[++num] = tr[u].val;
}
if (rs(u)) dfs(rs(u),k);
}

inline void del(int l,int r,int k){
int x,y,z;
split(rt[k],r,x,y); split(x,l - 1,x,z);
num = 0;
if (z) dfs(z,k);
rt[k] = merge(merge(x,build(1,num)),y);
}

#undef ls
#undef rs
}FT;

signed main(){
n = read(),q = read();
for (re int i = 1;i <= n;i++){
vis[arr[i] = read()] = true;
BT.modify(i,arr[i]);
}
for (re int i = 2;i <= 5e5;i++){
for (re int j = i;j <= 5e5;j += i){
if (vis[j]) p[j].emplace_back(i);
}
}
for (re int i = 1;i <= n;i++){
for (auto x:p[arr[i]]) v[x].emplace_back(i);
}
for (re int i = 2;i <= 5e5;i++){
num = 0;
for (auto x:v[i]) tmp[++num] = x;
rt[i] = FT.build(1,num);
}
while (q--){
int op,l,r; op = read(),l = read() ^ lst,r = read() ^ lst;
if (op == 1){
int k; k = read() ^ lst;
if (k > 1) FT.del(l,r,k);
}
else printf("%lld\n",lst = (BT.query(r) - BT.query(l - 1)));
}
return 0;
}

P5610 [Ynoi2013] 大学
http://watersun.top/[题解]P5610 [Ynoi2013] 大学/
作者
WaterSun
发布于
2024年4月29日
许可协议