记得分块还是大一时学的,后来就没怎么写过,趁此时机,再重新巩固一下此专题。
1.普通分块题目
后悔没有学习这么优秀的代码,自己当年写的就是一坨屎。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <cstdio> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int maxn = 1e5 + 50; int L[maxn], R[maxn]; ///每个区间范围 int pos[maxn]; ///记录所属块 ll sum[maxn], a[maxn]; ll add[maxn]; ///相当于lazy void change(int l, int r, int d) { int p = pos[l], q = pos[r]; if(p == q) ///所属同一块 { for(int i = l; i <= r; i++) a[i] += d; sum[p] += (ll)(r - l + 1) * d; } else ///不同块 { for(int i = p + 1; i <= q - 1; i++) add[i] += d; for(int i = l; i <= R[p]; i++) a[i] += d; sum[p] += (ll)(R[p] - l + 1) * d; for(int i = L[q]; i <= r; i++) a[i] += d; sum[q] += (ll)(r - L[q] + 1) * d; } } ll query(int l, int r) { int p = pos[l], q = pos[r]; ll ans = 0; if(p == q) ///同一块 { for(int i = l; i <= r; i++) ans += a[i]; ans += add[p] * (r - l + 1); } else ///不同块 { for(int i = p + 1; i <= q - 1; i++) ans += sum[i] + add[i] * (R[i] - L[i] + 1); for(int i = l; i <= R[p]; i++) ans += a[i]; ans += add[p] * (R[p] - l + 1); for(int i = L[q]; i <= r; i++) ans += a[i]; ans += add[q] * (r - L[q] + 1); } return ans; } int main() { int n, q; scanf("%d %d", &n, &q); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); int t = sqrt(n); for(int i = 1; i <= t; i++) { L[i] = (i - 1) * t + 1; R[i] = i * t; } if(R[t] < n) t++, L[t] = R[t - 1] + 1, R[t] = n; ///即使剩下的比sqrt(n)大也无所谓 for(int i = 1; i <= t; i++) { for(int j = L[i]; j <= R[i]; j++) { pos[j] = i; sum[i] += a[j]; } } while(q--) { char s[5]; int l, r, v; scanf("%s", s); if(s[0] == 'Q') { scanf("%d %d", &l, &r); printf("%lld ", query(l, r)); } else { scanf("%d %d %d", &l, &r, &v); change(l, r, v); } } return 0; }
这个一定要会算块大小呀,块大小不正确就是AC和TLE的区别呀!
假设每一块的大小都是$x$,那么分成$frac{n}{x}$个块,预处理的复杂度是$n imes frac{n}{x}$,询问的复杂度是$m imes x imes log(n)$,$log(n)$是每一个数字二分的复杂度,假设$m$和$n$同大小,那么最后复杂度是$n imes ( frac{n}{x} + x imes log(n))$,令加号两边相等,得$x=sqrt(frac{n}{log(n)})$。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cmath> #include <vector> using namespace std; const int maxn = 1e5 + 50; const int block = 3000; int L[block], R[block], num[block][block]; ///记录区间的答案 int pos[maxn]; int cnt[maxn], last[maxn]; vector<int> fu[maxn]; int a[maxn]; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int _read(){ char ch=nc();int sum=0; while(!(ch>='0'&&ch<='9'))ch=nc(); while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc(); return sum; } int query(int i, int l, int r) { int pos1 = lower_bound(fu[i].begin(), fu[i].end(), l) - fu[i].begin(); int pos2 = upper_bound(fu[i].begin(), fu[i].end(), r) - fu[i].begin(); return (pos2 - pos1) >= 0 ? (pos2 - pos1) : 0; } int main() { int n, c, m; n = _read(), c = _read(), m = _read(); for(int i = 1; i <= n; i++) { a[i] = _read(); fu[a[i]].push_back(i); } int t = sqrt((double)n / log(n) * log(2)); ///每个块的大小 int tot = n / t; for(int i = 1; i <= tot; i++) { L[i] = (i - 1) * t + 1; if(i == tot) R[i] = n; else R[i] = i * t; } // printf("%d %d ", t, tot); for(int i = 1; i <= tot; i++) { num[i][i - 1] = 0; for(int j = i; j <= tot; j++) { num[i][j] = num[i][j - 1]; for(int k = L[j]; k <= R[j]; k++) { pos[k] = j; if(last[a[k]] < i) ///有了last,就不用每次清空cnt数组了 { last[a[k]] = i; cnt[a[k]] = 1; } else cnt[a[k]]++; if(cnt[a[k]] % 2 == 0) num[i][j]++; else if(cnt[a[k]] > 1) num[i][j]--; // printf("%d %d %d ", i, j, num[i][j]); } } } memset(last, 0, sizeof(last)); int ans = 0; int k = 0; while(m--) { k++; int l, r; l = _read(), r = _read(); l = (l + ans) % n + 1; r = (r + ans) % n + 1; if(l > r) swap(l, r); int p = pos[l], q = pos[r]; // printf("%d %d %d %d ", l, r, p, q); ans = 0; if(p == q) { for(int i = l; i <= r; i++) { if(last[a[i]] < k) { last[a[i]] = k; ans += (query(a[i], l, r) & 1 ^ 1); } } printf("%d ", ans); } else { ans = num[p + 1][q - 1]; for(int i = l; i <= R[p]; i++) { if(last[a[i]] < k) { last[a[i]] = k; int x = query(a[i], L[p + 1], R[q - 1]); if(!x || (x & 1)) { ans += (query(a[i], l, r) & 1 ^ 1); } else ///之前为正偶数 { ans -= (query(a[i], l, r) & 1); } } } for(int i = L[q]; i <= r; i++) { if(last[a[i]] < k) { last[a[i]] = k; int x = query(a[i], L[p + 1], R[q - 1]); if(!x || (x & 1)) { ans += (query(a[i], l, r) & 1 ^ 1); } else ///之前为正偶数 { ans -= (query(a[i], l, r) & 1); } } } printf("%d ", ans); } } return 0; }
分块后,在每一块内记录通过几步弹出这个块,以及弹出这个块后的位置。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cmath> #include <vector> using namespace std; const int maxn = 2e5 + 50; int a[maxn]; int sum[maxn]; int nex[maxn]; int L[maxn], R[maxn]; int pos[maxn]; int t; void change(int j, int d) { a[j] = d; int p = pos[j]; for(int i = j; i >= L[p]; i--) { if(i + a[i] > R[p]) { sum[i] = 1; nex[i] = i + a[i]; } else { sum[i] = sum[i + a[i]] + 1; nex[i] = nex[i + a[i]]; } } } int n, m; int ask(int j) { int ans = 0; while(1) { ans += sum[j]; int nexpos = nex[j]; if(nexpos > n) break; j = nexpos; } return ans; } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); t = sqrt(n); for(int i = 1; i <= t; i++) { L[i] = (i - 1) * t + 1; if(i == t) R[i] = n; else R[i] = i * t; } ///预处理 for(int i = 1; i <= t; i++) { for(int j = R[i]; j >= L[i]; j--) { pos[j] = i; if(j + a[j] > R[i]) { sum[j] = 1; nex[j] = j + a[j]; } else { sum[j] = sum[j + a[j]] + 1; nex[j] = nex[j + a[j]]; } } } scanf("%d", &m); while(m--) { int op, j, k; scanf("%d", &op); if(op == 1) { scanf("%d", &j); j++; if(j > n) printf("0 "); else printf("%d ", ask(j)); } else { scanf("%d %d", &j, &k); j++; change(j, k); } } } /*4 1 2 1 1 412 2 0 10 1 0 2 0 1 2 1 10 1 0 */
2.树上分块题目
Codeforces Round #199 (Div. 2) E. Xenia and Tree
这种分块还是挺有意思的,等红点到达一定数量后,再重新建图。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 50; const int INF = 0x3f3f3f3f; vector<int> red; queue<int> q; int vis[maxn]; int d[maxn]; int n, m; ///加边 int cnt, h[maxn]; struct edge { int to, pre, v; } e[maxn << 1]; void init() { cnt = 0; memset(h, 0, sizeof(h)); } void add(int from, int to, int v) { cnt++; e[cnt].pre = h[from]; ///5-->3-->1-->0 e[cnt].to = to; e[cnt].v = v; h[from] = cnt; } ///LCA int dist[maxn]; int dep[maxn]; int anc[maxn][33]; ///2分的父亲节点 void dfs(int u, int fa) { for(int i = h[u]; i; i = e[i].pre) { int v = e[i].to; if(v == fa) continue; dist[v] = dist[u] + e[i].v; dep[v] = dep[u] + 1; anc[v][0] = u; dfs(v, u); } } void LCA_init(int n) { for(int j = 1; (1 << j) < n; j++) for(int i = 1; i <= n; i++) if(anc[i][j-1]) anc[i][j] = anc[anc[i][j-1]][j-1]; } int LCA(int u, int v) { int log; if(dep[u] < dep[v]) swap(u, v); for(log = 0; (1 << log) < dep[u]; log++); for(int i = log; i >= 0; i--) if(dep[u] - (1 << i) >= dep[v]) u = anc[u][i]; if(u == v) return u; for(int i = log; i >= 0; i--) if(anc[u][i] && anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i]; return anc[u][0]; } int calc(int u, int v) { return dist[u] + dist[v] - 2 * dist[LCA(u, v)]; } void bfs() { while(!q.empty()) q.pop(); // for(int i = 1; i <= n; i++) vis[i] = 0; for(int i = 0; i < (int)red.size(); i++) { int u = red[i]; d[u] = 0; // vis[u] = 1; q.push(u); } while(!q.empty()) { int u = q.front(); q.pop(); for(int i = h[u]; i; i = e[i].pre) { int v = e[i].to; if(d[v] > d[u] + 1) { //vis[v] = 1; d[v] = d[u] + 1; q.push(v); } } } } int main() { memset(d, INF, sizeof(d)); init(); scanf("%d %d", &n, &m); for(int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); add(u, v, 1), add(v, u, 1); } red.push_back(1); bfs(); red.clear(); dfs(1, -1); LCA_init(n); int block = sqrt(n); for(int i = 1; i <= m; i++) { int t, v; scanf("%d %d", &t, &v); if(t == 1) { red.push_back(v); if(((int)red.size()) == block) { bfs(); red.clear(); } } else { int ans = d[v]; for(int j = 0; j < (int)red.size(); j++) { ans = min(ans, calc(v, red[j])); } printf("%d ", ans); } } return 0; }
和我想象中的树上分块不太一样,按DFS序走完后,就是普通的序列呀。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn =5e4 + 50; vector<int> g[maxn]; int loy[maxn], abi[maxn]; int in[maxn], out[maxn]; int ind = 0; int match[maxn]; int rec[maxn]; void dfs(int u) { in[u] = ++ind; rec[ind] = ind; match[ind] = u; for(int i = 0; i < g[u].size(); i++) { int v = g[u][i]; dfs(v); } out[u] = ind; } int t; int L[maxn], R[maxn]; bool cmp(int a, int b) { int va = match[a]; int vb = match[b]; if(abi[va] != abi[vb]) return abi[va] < abi[vb]; else return loy[va] > loy[vb]; } int maxloy[maxn]; int pos[maxn]; int query(int v, int l, int r) { int p = pos[l], q = pos[r]; int ans = -1; int tmploy = 0; if(p == q) { for(int i = l; i <= r; i++) ///不够整段,就暴力枚举 { if(abi[match[i]] > abi[v]) { if(loy[match[i]] > tmploy) { ans = match[i]; tmploy = loy[match[i]]; } } } } else { for(int i = p + 1; i <= q - 1; i++) { int l = L[i], r = R[i]; int tmp = 0; while(l <= r) { int mid = (l + r) / 2; int u = match[rec[mid]]; if(abi[u] > abi[v]) { tmp = maxloy[mid]; r = mid - 1; } else { l = mid + 1; } } if(loy[tmp] > tmploy) { ans = tmp; tmploy = loy[tmp]; } } for(int i = l; i <= R[p]; i++) { if(abi[match[i]] > abi[v]) { if(loy[match[i]] > tmploy) { ans = match[i]; tmploy = loy[match[i]]; } } } for(int i = L[q]; i <= r; i++) { if(abi[match[i]] > abi[v]) { if(loy[match[i]] > tmploy) { ans = match[i]; tmploy = loy[match[i]]; } } } } // printf("%d ", ans); return ans; } int main() { //freopen("a.txt", "r", stdin); // freopen("wrong.txt", "w", stdout); int T; scanf("%d", &T); while(T--) { ind = 0; int n, m; scanf("%d %d", &n, &m); for(int i = 0; i < n; i++) g[i].clear(); loy[0] = 1e6 + 5, abi[0] = 1e6 + 5; for(int i = 1; i <= n - 1; i++) { int a; scanf("%d %d %d", &a, &loy[i], &abi[i]); g[a].push_back(i); } dfs(0); t = sqrt(ind); for(int i = 1; i <= t; i++) { if(i == 1) L[i] = 2; else L[i] = (i - 1) * t + 1; if(i == t) R[i] = ind; else R[i] = i * t; } // printf("%d ", t); ///预处理 for(int i = 1; i <= t; i++) { sort(rec + L[i], rec + R[i] + 1, cmp); ///排序这里要注意 for(int j = R[i]; j >= L[i]; j--) { pos[j] = i; if(j == R[i]) maxloy[j] = match[rec[j]]; else { int v = match[rec[j]]; if(loy[v] > loy[maxloy[j + 1]]) ///从后往前记录最大的忠诚度对应的人 { maxloy[j] = v; } else { maxloy[j] = maxloy[j + 1]; } } } } while(m--) { int fired; scanf("%d", &fired); printf("%d ", query(fired, in[fired], out[fired])); } } return 0; } /* 312 12 10 0 976 11305 0 20237 7816 1 18781 12029 3 23992 24775 4 31529 8581 0 24804 14400 3 20559 5900 6 26811 17570 4 4405 10627 7 8046 27226 5 899 16169 1 */