根号算法
分块与莫队
1.分块
大概就是把序列分成若干块,预处理出一些东西,整块打标记,边角暴力。
当然不一定是对着序列分块。可能对着任何东西分块,比如哈希冲突...
复杂度随着题目相应变化,一般用均值不等式来确定块的大小。
例题首先是hzwer的分块9题。
说两个神奇的分块思想。
1.当暴力复杂度是n²m,且每个元素贡献独立的时候,可以把这些元素分成n0.5块,分别暴力。
总复杂度是n1.5m(有用吗...)
2.对时间/操作分块。
把操作分成若干块,块大小是T。之后对于每一块,预处理之前的影响,暴力查块内的影响。
总复杂度是O(预处理复杂度 * T + m² / T * 暴力查询复杂度)
2.莫队
先%%%莫涛。
两个指针跳来跳去......
普通莫队:就是离线,把询问排序,使得挪动指针的总次数不大于n1.5。
例题:小Z的袜子 小B的询问 大爷的字符串 小清新人渣的本愿
带修莫队:三个维度排序。时间作为第三关键字。
相当于三个指针跳来跳去,第三个指针在时间上。这样就懂了吧。
树上莫队:用括号序列转成序列莫队。
先讲一下括号序列,DFS序,欧拉序的区别。
- DFS序就是DFS时,第一次进入点时将其加入序列,长度为n。树剖就是用的一种DFS序。
- 括号序列:出来的时候额外加入一次,长度为2n。
- 欧拉序:每次切换节点时,将当前节点加入序列。长度为2n - 1,因为每个非根节点都会自己加入一次,让父亲加入一次。
把括号序列搞出来。我们统计其中出现一次的点。
对于路径x - y的询问,先让first[x] < first[y]
求lca:z。如果z == x,那么直接用first[x] - first[y]
否则就用last[x] - first[y],然后查询时加上z。
此处的first和last表示第一,二次在序列中出现的位置。
例题:SP10707 Count on a tree II
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 5 const int N = 200010; 6 7 struct Edge { 8 int nex, v; 9 }edge[N << 1]; int top; 10 11 int fr[N], e[N], a[N], first[N], last[N], num, val[N], X[N], fa[N][20], pw[N], d[N], n, ans, bin[N]; 12 bool vis[N]; 13 14 struct ASK { 15 int l, r, t, ans, ex; 16 inline bool operator <(const ASK &w) const { 17 if(fr[l] != fr[w.l]) { 18 return l < w.l; 19 } 20 return r < w.r; 21 } 22 }ask[N]; 23 24 inline bool cmp(const ASK &A, const ASK &B) { 25 return A.t < B.t; 26 } 27 28 inline void add(int x, int y) { 29 top++; 30 edge[top].v = y; 31 edge[top].nex = e[x]; 32 e[x] = top; 33 return; 34 } 35 36 void DFS(int x, int f) { 37 a[++num] = x; 38 first[x] = num; 39 fa[x][0] = f; 40 d[x] = d[f] + 1; 41 for(int i = e[x]; i; i = edge[i].nex) { 42 int y = edge[i].v; 43 if(y == f) { 44 continue; 45 } 46 DFS(y, x); 47 } 48 a[++num] = x; 49 last[x] = num; 50 return; 51 } 52 53 inline int lca(int x, int y) { 54 if(d[x] > d[y]) { 55 std::swap(x, y); 56 } 57 int t = pw[n]; 58 while(t >= 0 && d[x] != d[y]) { 59 if(d[fa[y][t]] >= d[x]) { 60 y = fa[y][t]; 61 } 62 t--; 63 } 64 if(x == y) { 65 return x; 66 } 67 t = pw[n]; 68 while(t >= 0 && fa[x][0] != fa[y][0]) { 69 if(fa[x][t] != fa[y][t]) { 70 x = fa[x][t]; 71 y = fa[y][t]; 72 } 73 t--; 74 } 75 return fa[x][0]; 76 } 77 78 inline void add(int x) { 79 if(!bin[val[x]]) { 80 ans++; 81 } 82 bin[val[x]]++; 83 return; 84 } 85 86 inline void del(int x) { 87 bin[val[x]]--; 88 if(!bin[val[x]]) { 89 ans--; 90 } 91 return; 92 } 93 94 inline void work(int p) { 95 int x = a[p]; 96 if(vis[x]) { 97 del(x); 98 } 99 else { 100 add(x); 101 } 102 vis[x] ^= 1; 103 return; 104 } 105 106 int main() { 107 int m; 108 scanf("%d%d", &n, &m); 109 for(int i = 2; i <= n; i++) { 110 pw[i] = pw[i >> 1] + 1; 111 } 112 int T = sqrt(n << 1); 113 for(int i = 1; i <= n; i++) { 114 scanf("%d", &val[i]); 115 X[i] = val[i]; 116 } 117 std::sort(X + 1, X + n + 1); 118 int temp = std::unique(X + 1, X + n + 1) - X - 1; 119 for(int i = 1; i <= n; i++) { 120 val[i] = std::lower_bound(X + 1, X + temp + 1, val[i]) - X; 121 } 122 for(int i = 1, x, y; i < n; i++) { 123 scanf("%d%d", &x, &y); 124 add(x, y); 125 add(y, x); 126 } 127 DFS(1, 0); 128 for(int j = 1; j <= pw[n]; j++) { 129 for(int i = 1; i <= n; i++) { 130 fa[i][j] = fa[fa[i][j - 1]][j - 1]; 131 } 132 } 133 for(int i = 1; i <= n * 2; i++) { 134 fr[i] = (i - 1) / T + 1; 135 } 136 for(int i = 1, x, y; i <= m; i++) { 137 scanf("%d%d", &x, &y); 138 if(first[x] > first[y]) { 139 std::swap(x, y); 140 } 141 int z = lca(x, y); 142 if(z == x) { 143 ask[i].l = first[x]; 144 ask[i].r = first[y]; 145 } 146 else { 147 ask[i].l = last[x]; 148 ask[i].r = first[y]; 149 ask[i].ex = z; 150 } 151 ask[i].t = i; 152 } 153 154 std::sort(ask + 1, ask + m + 1); 155 int l = 1, r = 1; 156 work(1); 157 158 for(int i = 1; i <= m; i++) { 159 while(l > ask[i].l) { 160 l--; 161 work(l); 162 } 163 while(r < ask[i].r) { 164 r++; 165 work(r); 166 } 167 while(l < ask[i].l) { 168 work(l); 169 l++; 170 } 171 while(r > ask[i].r) { 172 work(r); 173 r--; 174 } 175 if(ask[i].ex) { 176 add(ask[i].ex); 177 } 178 ask[i].ans = ans; 179 if(ask[i].ex) { 180 del(ask[i].ex); 181 } 182 } 183 184 std::sort(ask + 1, ask + m + 1, cmp); 185 for(int i = 1; i <= m; i++) { 186 printf("%d\n", ask[i].ans); 187 } 188 return 0; 189 }
可以发现上树之后代码量大了很多.....
树上带修莫队:前面两种嵌套一下就行了。
经典例题是糖果公园,码量有点大,不过主要是卡常。事实证明O2好用的一批......
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 5 typedef long long LL; 6 const int N = 400010; 7 8 template<class T> inline void read(T &x) { 9 x = 0; 10 char c = getchar(); 11 while(c < '0' || c > '9') { 12 c = getchar(); 13 } 14 while(c >= '0' && c <= '9') { 15 x = (x << 3) + (x << 1) + c - 48; 16 c = getchar(); 17 } 18 return; 19 } 20 21 struct Edge { 22 int nex, v; 23 }edge[N << 1]; int top; 24 25 int fr[N], e[N], a[N], bin[N], first[N], last[N], num, fa[N][20], d[N], n, col[N], pw[N]; 26 LL ans, W[N], val[N]; 27 bool vis[N]; 28 29 struct ASK { 30 int l, r, t, ex; 31 LL ans; 32 inline bool operator <(const ASK &w) const { 33 if(fr[l] != fr[w.l]) { 34 return l < w.l; 35 } 36 if(r != w.r) { 37 return r < w.r; 38 } 39 return t < w.t; 40 } 41 }ask[N]; int topa; 42 43 struct Change { 44 int p, t, x, last; 45 }change[N]; int topc; 46 47 inline bool cmp(const ASK &A, const ASK &B) { 48 return A.t < B.t; 49 } 50 51 inline void add(int x, int y) { 52 top++; 53 edge[top].v = y; 54 edge[top].nex = e[x]; 55 e[x] = top; 56 return; 57 } 58 59 void DFS(int x, int f) { 60 a[++num] = x; 61 first[x] = num; 62 fa[x][0] = f; 63 d[x] = d[f] + 1; 64 for(int i = e[x]; i; i = edge[i].nex) { 65 int y = edge[i].v; 66 if(y == f) { 67 continue; 68 } 69 DFS(y, x); 70 } 71 a[++num] = x; 72 last[x] = num; 73 return; 74 } 75 76 inline int lca(int x, int y) { 77 if(d[x] > d[y]) { 78 std::swap(x, y); 79 } 80 int t = pw[n]; 81 while(t >= 0 && d[y] > d[x]) { 82 if(d[fa[y][t]] >= d[x]) { 83 y = fa[y][t]; 84 } 85 t--; 86 } 87 if(x == y) { 88 return x; 89 } 90 t = pw[n]; 91 while(t >= 0 && fa[x][0] != fa[y][0]) { 92 if(fa[x][t] != fa[y][t]) { 93 x = fa[x][t]; 94 y = fa[y][t]; 95 } 96 t--; 97 } 98 return fa[x][0]; 99 } 100 101 inline void add(int x) { 102 //bin[col[x]]++; 103 (*(bin + (*(col + x))))++; 104 //ans += W[bin[col[x]]] * val[col[x]]; 105 ans += (*(W + (*(bin + (*(col + x)))))) * (*(val + (*(col + x)))); 106 return; 107 } 108 109 inline void del(int x) { 110 //ans -= W[bin[col[x]]] * val[col[x]]; 111 ans -= (*(W + (*(bin + (*(col + x)))))) * (*(val + (*(col + x)))); 112 //bin[col[x]]--; 113 (*(bin + (*(col + x))))--; 114 return; 115 } 116 117 inline void work(int p) { 118 int x = *(a + p); 119 (*(vis + x)) ? del(x) : add(x); 120 vis[x] ^= 1; 121 return; 122 } 123 124 int main() { 125 int m, q; 126 read(n); 127 read(m); 128 read(q); 129 int T = pow(n << 1, 2.0 / 3.0); 130 for(int i = 1; i <= m; i++) { 131 read(val[i]); 132 } 133 for(int i = 1; i <= n; i++) { 134 read(W[i]); 135 } 136 for(int i = 1, x, y; i < n; i++) { 137 read(x); 138 read(y); 139 add(x, y); 140 add(y, x); 141 } 142 for(int i = 1; i <= n; i++) { 143 read(col[i]); 144 } 145 146 DFS(1, 0); 147 for(int i = 2; i <= n; i++) { 148 pw[i] = pw[i >> 1] + 1; 149 } 150 for(int j = 1; j <= pw[n]; j++) { 151 for(int i = 1; i <= n; i++) { 152 fa[i][j] = fa[fa[i][j - 1]][j - 1]; 153 } 154 } 155 156 for(int i = 1, f, x, y; i <= q; i++) { 157 read(f); 158 read(x); 159 read(y); 160 if(!f) { // change 161 ++topc; 162 change[topc].p = x; 163 change[topc].x = y; 164 change[topc].t = i; 165 } 166 else { // ask 167 ++topa; 168 if(first[x] > first[y]) { 169 std::swap(x, y); 170 } 171 int z = lca(x, y); 172 if(z == x) { 173 ask[topa].l = first[x]; 174 ask[topa].r = first[y]; 175 } 176 else { 177 ask[topa].l = last[x]; 178 ask[topa].r = first[y]; 179 ask[topa].ex = z; 180 } 181 ask[topa].t = i; 182 } 183 } 184 for(int i = 1; i <= n * 2; i++) { 185 fr[i] = (i - 1) / T + 1; 186 } 187 std::sort(ask + 1, ask + topa + 1); 188 189 int l = 1, r = 1, time = 0; 190 work(1); 191 for(int i = 1; i <= topa; i++) { 192 while(ask[i].l < l) { 193 work(--l); 194 } 195 while(r < ask[i].r) { 196 work(++r); 197 } 198 while(l < ask[i].l) { 199 work(l++); 200 } 201 while(ask[i].r < r) { 202 work(r--); 203 } 204 while(change[time].t > ask[i].t) { 205 bool f = *(vis + change[time].p); 206 if(f) { 207 del(change[time].p); 208 } 209 *(col + change[time].p) = change[time].last; 210 if(f) { 211 add(change[time].p); 212 } 213 time--; 214 } 215 while(time < topc && change[time + 1].t <= ask[i].t) { 216 time++; 217 bool f = *(vis + change[time].p); 218 change[time].last = *(col + change[time].p); 219 if(f) { 220 del(change[time].p); 221 } 222 *(col + change[time].p) = change[time].x; 223 if(f) { 224 add(change[time].p); 225 } 226 } 227 if(ask[i].ex) { 228 add(ask[i].ex); 229 } 230 ask[i].ans = ans; 231 if(ask[i].ex) { 232 del(ask[i].ex); 233 } 234 } 235 236 std::sort(ask + 1, ask + topa + 1, cmp); 237 for(int i = 1; i <= topa; i++) { 238 printf("%lld\n", ask[i].ans); 239 } 240 return 0; 241 }
回滚莫队:解决不好删除,只能增加的莫队。
例:歴史の研究