点分治
什么是点分治?
点分治主要用于有关树上路径统计的问题。
怎么点分治?
1,选取一个点,把树变成有根树。为了让递归层数尽可能的小,我们要选取树的重心,即子树大小最大值最小的点。
2,处理联通块中通过根的路径。
3,删除根节点。
4,递归处理子树。
操作
1 void getR(int u, int f) { 2 sz[u] = 1; mx[u] = 0; 3 for (int v, i = 0; i < g[u].size(); ++i) if ((v = g[u][i].v) != f && !vis[v]) { 4 getR(v, u); sz[u] += sz[v]; gmax(mx[u], sz[v]); 5 } gmax(mx[u], size - sz[u]); 6 if (mx[R] > mx[u]) R = u; 7 } 8 //找重心
例题们
POJ - 1741 BZOJ - 1468 BZOJ - 1316
1 #include<algorithm> 2 #include<vector> 3 #include<cstdio> 4 #include<bitset> 5 #include<iostream> 6 using namespace std; 7 inline char nc() { 8 static char b[1<<15],*s=b,*t=b; 9 return s==t&&(t=(s=b)+fread(b,1,1<<15,stdin),s==t)?-1:*s++; 10 } 11 inline void read(int &x) { 12 char b = nc(); x = 0; 13 for (; !isdigit(b); b = nc()); 14 for (; isdigit(b); b = nc()) x = x * 10 + b - ‘0‘; 15 } 16 const int N = 10010; 17 int n, m, R, sz[N], msz[N], ans, size, dep[N], D, d[N]; 18 struct Edge {int v,w;}; 19 vector < Edge > g[N]; 20 bitset < N > vis; 21 inline void ae(int u, int v, int w) { 22 g[u].push_back((Edge){v, w}); 23 g[v].push_back((Edge){u, w}); 24 } 25 void getR(int u, int f) { 26 sz[u] = 1; msz[u] = 0; 27 for (int v, i = 0; i < g[u].size(); ++i) if ((v = g[u][i].v) != f && !vis[v]) { 28 getR(v, u); sz[u] += sz[v]; msz[u] = max(msz[u], sz[v]); 29 } msz[u] = max(msz[u], size - sz[u]); 30 if (msz[u] < msz[R]) R = u; 31 } 32 void getD(int u, int f) { 33 d[++D] = dep[u]; 34 for (int v, i = 0; i < g[u].size(); ++i) 35 if ((v = g[u][i].v) != f && !vis[v]) 36 dep[v] = dep[u] + g[u][i].w, getD(v, u); 37 } 38 int calc(int u) { 39 D = 0; getD(u, 0); 40 sort(d + 1, d + 1 + D); 41 int res = 0, l = 1, r = D; 42 while (l < r) { 43 if (d[l] + d[r] <= m) res += r - l, ++l; 44 else --r; 45 } return res; 46 } 47 void solve(int u) { 48 dep[u] = 0; vis[u] = 1; ans += calc(u); 49 for (int v, i = 0; i < g[u].size(); ++i) { 50 if (!vis[v = g[u][i].v]) { 51 dep[v] = g[u][i].w; 52 ans -= calc(v); 53 size = sz[v]; 54 getR(v, R = 0); 55 solve(R); 56 } 57 } 58 } 59 void solve() { 60 for (int i = 1, u, v, w; i < n; ++i) 61 read(u), read(v), read(w), ae(u, v, w); 62 size = n; getR(1, R = 0); solve(R); 63 printf("%d\n", ans); ans = 0; 64 for (int i = 1; i <= n; ++i) g[i].clear(); 65 vis.reset(); 66 } 67 int main() { 68 msz[0] = 0x3f3f3f3f; 69 while (read(n), read(m), n + m) solve(); 70 return 0; 71 }
1 #include<set> 2 #include<bitset> 3 #include<vector> 4 #include<cstdio> 5 #include<iostream> 6 #define fir first 7 #define pb push_back 8 #define sec second 9 using namespace std; 10 inline char nc() { 11 static char b[1<<16],*s=b,*t=b; 12 return s==t&&(t=(s=b)+fread(b,1,1<<16,stdin),s==t)?-1:*s++; 13 } 14 template < class T > 15 inline void read(T &x) { 16 char b = nc(); x = 0; 17 for (; !isdigit(b); b = nc()); 18 for (; isdigit(b); b = nc()) x = x * 10 + b - ‘0‘; 19 } 20 typedef long long ll; 21 const int N = 10010; 22 int n, m, sz[N], R, mx[N], size; 23 ll dis[N]; 24 set < int > s; 25 bitset < N > vis; 26 struct Edge {int v,w;}; 27 pair < ll , bool > q[110]; 28 vector < Edge > g[N]; 29 inline void ae(int u, int v, int w) { 30 g[u].pb((Edge){v, w}); 31 g[v].pb((Edge){u, w}); 32 } 33 inline void gmax(int &x, int y) { 34 if (x < y) x = y; 35 } 36 void getR(int u, int f) { 37 sz[u] = 1; mx[u] = 0; 38 for (int v, i = 0; i < g[u].size(); ++i) if ((v = g[u][i].v) != f && !vis[v]) { 39 getR(v, u); sz[u] += sz[v]; gmax(mx[u], sz[v]); 40 } gmax(mx[u], size - sz[u]); 41 if (mx[R] > mx[u]) R = u; 42 } 43 inline void upd(ll x) { 44 for (int i = 1; i <= m; ++i) 45 if (!q[i].sec && s.count(q[i].fir - x)) q[i].sec = 1; 46 } 47 void calc(int u, int f) { 48 upd(dis[u]); 49 for (int i = 0, v; i < g[u].size(); ++i) 50 if ((v = g[u][i].v) != f && !vis[v]) 51 dis[v] = dis[u] + g[u][i].w, calc(v, u); 52 } 53 void add(int u, int f) { 54 s.insert(dis[u]); 55 for (int i = 0, v; i < g[u].size(); ++i) 56 if ((v = g[u][i].v) != f && !vis[v]) add(v, u); 57 } 58 void solve(int u) { 59 vis[u] = 1; dis[u] = 0; s.clear(); s.insert(0); 60 for (int v, i = 0; i < g[u].size(); ++i) if (!vis[v = g[u][i].v]) { 61 dis[v] = g[u][i].w; calc(v, u); add(v, u); 62 } 63 for (int v, i = 0; i < g[u].size(); ++i) if (!vis[v = g[u][i].v]) { 64 size = sz[v]; getR(v, R = 0); solve(R); 65 } 66 } 67 int main() { 68 read(n); read(m); mx[0] = ~0U >> 1; 69 for (int u, v, w, i = 1; i < n; ++i) 70 read(u), read(v), read(w), ae(u, v, w); 71 for (int i = 1; i <= m; ++i) read(q[i].fir); 72 size = n; getR(1, 1); solve(R); 73 for (int i = 1; i <= m; ++i) puts(q[i].fir == 0 || q[i].sec ? "Yes" : "No"); 74 return 0; 75 } 76 //bzoj1316
入门题。
题意:找出距离$ \leqslant m$的点对的数目。
为了不重复计算,咱每次都统计必须经过根节点的满足条件的路径的条数。
要统计在某子树中满足条件的路径,咱只要把子树中每一点到根的距离都算出来,排序,扫一次就行。
必须经过根节点的满足条件的路径的条数为$calc(u) - \Sigma calc(v)$。
[IOI2011]Race
1 #include<bitset> 2 #include<cstdio> 3 #include<vector> 4 #include<iostream> 5 using namespace std; 6 inline char nc() { 7 static char b[1<<16],*s=b,*t=b; 8 return s==t&&(t=(s=b)+fread(b,1,1<<16,stdin),s==t)?-1:*s++; 9 } 10 inline void read(int &x) { 11 char b = nc(); x = 0; 12 for (; !isdigit(b); b = nc()); 13 for (; isdigit(b); b = nc()) x = x * 10 + b - ‘0‘; 14 } 15 const int N = 200005; 16 struct Edge {int v,w;}; 17 vector < Edge > g[N]; 18 inline void ae(int u, int v, int w) { 19 g[u].push_back((Edge){v, w}); 20 g[v].push_back((Edge){u, w}); 21 } 22 int n, m, R, sz[N], mx[N], size, dep[N], ans, dis[N]; 23 bitset < N > vis; 24 int h[1000010], T[1000010], D; 25 inline void gmin(int &x, int y) {if (x > y) x = y;} 26 inline void gmax(int &x, int y) {if (x < y) x = y;} 27 void getR(int u, int f) { 28 sz[u] = 1; mx[u] = 0; 29 for (int v, i = 0; i < g[u].size(); ++i) 30 if ((v = g[u][i].v) != f && !vis[v]) { 31 getR(v, u); sz[u] += sz[v]; gmax(mx[u], sz[v]); 32 } gmax(mx[u], size - sz[u]); 33 if (mx[R] > mx[u]) R = u; 34 } 35 inline void query(int x, int y) { 36 if (x <= m && T[m-x] == D) gmin(ans, y + h[m-x]); 37 } 38 inline void upd(int x, int y) { 39 if (T[x] == D) gmin(h[x], y); else h[x] = y, T[x] = D; 40 } 41 void calc(int u, int f) { 42 dep[u] = dep[f] + 1; query(dis[u], dep[u]); 43 for (int v, i = 0; i < g[u].size(); ++i) 44 if ((v = g[u][i].v) != f && !vis[v]) 45 dis[v] = dis[u] + g[u][i].w, calc(v, u); 46 } 47 void add(int u, int f) { 48 if (dis[u] <= m) upd(dis[u], dep[u]); 49 for (int v, i = 0; i < g[u].size(); ++i) 50 if ((v = g[u][i].v) != f && !vis[v]) add(v, u); 51 } 52 void solve(int u) { 53 vis[u] = 1; ++D; upd(dis[u] = 0, dep[u] = 0); 54 for (int v, i = 0; i < g[u].size(); ++i) 55 if (!vis[v = g[u][i].v]) { 56 dis[v] = g[u][i].w; 57 calc(v, u); add(v, u); 58 } 59 for (int v, i = 0; i < g[u].size(); ++i) { 60 if (!vis[v = g[u][i].v]) { 61 size = sz[v]; 62 getR(v, R = 0); 63 solve(R); 64 } 65 } 66 } 67 int main() { 68 read(n); read(m); ans = ~0U >> 1; mx[0] = 0x3f3f3f3f; 69 for (int i = 1, u, v, w; i < n; ++i) 70 read(u), read(v), read(w), ae(++u, ++v, w); 71 size = n; getR(1, 1); solve(R); 72 printf("%d\n", ans != ~0U >> 1 ? ans : -1); 73 return 0; 74 }
咱每次只统计经过了根节点的路径。所以要先统计子树答案在加入子树信息。
全局开一个数组,记录到根节点的长度为$x$时,最小的深度$h[x]$。
3697: 采药人的路径
1 #include<bitset> 2 #include<vector> 3 #include<cstdio> 4 #include<iostream> 5 using namespace std; 6 inline char nc() { 7 static char b[1<<16],*s=b,*t=b; 8 return s==t&&(t=(s=b)+fread(b,1,1<<16,stdin),s==t)?-1:*s++; 9 } 10 inline void read(int &x) { 11 char b = nc(); x = 0; 12 for (; !isdigit(b); b = nc()); 13 for (; isdigit(b); b = nc()) x = x * 10 + b - ‘0‘; 14 } 15 const int N = 100005; 16 struct Edge {int v,w;}; 17 vector < Edge > e[N]; 18 inline void ae(int u, int v, int w) { 19 e[u].push_back((Edge){v, w}); 20 e[v].push_back((Edge){u, w}); 21 } 22 int n, sz[N], mx[N], R, size, ff[N+N][2], gg[N+N][2], dis[N], dmx, hmx; 23 long long ans; 24 bitset < N > vis; 25 int (*f)[2] = ff + 100000, (*g)[2] = gg + 100000; 26 int cc[N+N], *c = cc + 100000; 27 inline int ABS(int x) {return x > 0 ? x : -x;} 28 inline void gmax(int &x, int y) { 29 if (x < y) x = y; 30 } 31 void getR(int u, int f) { 32 sz[u] = 1; mx[u] = 0; 33 for (int v, i = 0; i < e[u].size(); ++i) 34 if ((v = e[u][i].v) != f && !vis[v]) { 35 getR(v, u); sz[u] += sz[v]; gmax(mx[u], sz[v]); 36 } 37 gmax(mx[u], size - sz[u]); 38 if (mx[R] > mx[u]) R = u; 39 } 40 void calc(int u, int f) { 41 gmax(dmx, ABS(dis[u])); 42 ++g[dis[u]][bool(c[dis[u]])]; 43 ++c[dis[u]]; 44 for (int v, i = 0; i < e[u].size(); ++i) 45 if ((v = e[u][i].v) != f && !vis[v]) 46 dis[v] = dis[u] + e[u][i].w, calc(v, u); 47 --c[dis[u]]; 48 } 49 void solve(int u) { 50 f[dis[u] = 0][0] = 1; vis[u] = 1; hmx = 0; 51 for (int v, i = 0; i < e[u].size(); ++i) 52 if (!vis[v = e[u][i].v]) { 53 dmx = 0; dis[v] = e[u][i].w; 54 calc(v, u); gmax(hmx, dmx); 55 ans += (f[0][0]-1) * g[0][0]; 56 for (int j = -dmx; j <= dmx; ++j) 57 ans += f[j][0] * g[-j][1] + f[j][1] * g[-j][0] + f[j][1] * g[-j][1]; 58 for (int j = -dmx; j <= dmx; ++j) 59 f[j][0] += g[j][0], f[j][1] += g[j][1], g[j][0] = g[j][1] = 0; 60 } 61 for (int i = -hmx; i <= hmx; ++i) f[i][0] = f[i][1] = 0; 62 for (int v, i = 0; i < e[u].size(); ++i) 63 if (!vis[v = e[u][i].v]) { 64 size = sz[v]; 65 getR(v, R = 0); 66 solve(R); 67 } 68 } 69 int main() { 70 read(n); mx[0] = 0x3f3f3f3f; size = n; 71 for (int i = 1, u, v, w; i < n; ++i) 72 read(u), read(v), read(w), ae(u, v, w == 1 ? 1 : -1); 73 getR(1, 1); solve(R); 74 printf("%lld\n", ans); 75 return 0; 76 }
题意:边权有$1,-1$,求有多少条路径权值和为$0$,且从中间某点断开,剩下的两条路径的权值和也为$0$。
记$dis(u)$为当前根$r$到$u$的距离,$f(d,0/1)$为之前几棵子树中距离为$d$,是否出现过$d$这个权值的边的个数。$g(d,0/1)$则是当前子树的。
如果向下$dfs$时发现现在的距离$d$在$r-u$上是出现过的,说明$u$到之前出现$d$的点$v$的这一段距离为$0$。也就是说$u-v$边权和为$0$,用题目的说法就是阴阳平衡。
算出了$f$和$g$后就很好计算答案了,$ans = f(0,0) \times g(0,0) + \sum_d f(d,0) \times g(-d, 1) +f(d, 1) \times g(-d, 0) + f(d, 1) \times g(d, 1)$。
分别对应着休息点在根上,当前子树中,前几棵子树中的某一棵中,当前子树和前几棵子树的某一棵都行。
A1486. 树
1 #include<stack> 2 #include<cstdio> 3 #include<vector> 4 #include<bitset> 5 #include<iostream> 6 #define pb push_back 7 using namespace std; 8 inline char nc() { 9 static char b[1<<16],*s=b,*t=b; 10 return s==t&&(t=(s=b)+fread(b,1,1<<16,stdin),s==t)?-1:*s++; 11 } 12 inline void read(int &x) { 13 char b = nc(); x = 0; 14 for (; !isdigit(b); b = nc()); 15 for (; isdigit(b); b = nc()) x = x * 10 + b - ‘0‘; 16 } 17 const int N = 100005; 18 inline void gmax(int &x, int y) {if (x < y) x = y;} 19 struct Node { 20 Node *ch[2]; int v; 21 } Tnull, *null = &Tnull, *root; 22 Node mem[6000000], *C; 23 inline Node* newNode() { 24 C->ch[0] = C->ch[1] = null; 25 C->v = 0; return C++; 26 } 27 void insert(int x, int v) { 28 Node *u = root; 29 for (int c, i = 30; ~i; --i) { 30 c = (x >> i) & 1; 31 if (u->ch[c] == null) 32 u->ch[c] = newNode(); 33 u = u->ch[c]; 34 gmax(u->v, v); 35 } 36 } 37 int query(int x, int v) { 38 if (root == null) return -1; 39 int res = 0; 40 Node *u = root; 41 for (int c, i = 30; ~i; --i) { 42 c = !((x >> i) & 1); 43 if (u->ch[c] != null && u->ch[c]->v >= v) 44 res |= 1 << i; 45 else c = !c; 46 u = u->ch[c]; 47 if (u->v < v) return -1; 48 } return res; 49 } 50 vector < int > g[N]; 51 int n, m, val[N], sz[N], mx[N], R, ans, size; 52 bitset < N > vis, a; 53 inline void ae(int u, int v) { 54 g[u].pb(v); g[v].pb(u); 55 } 56 void getR(int u, int f) { 57 sz[u] = 1; mx[u] = 0; 58 for (int v,i = 0; i < g[u].size(); ++i) if ((v = g[u][i]) != f && !vis[v]) { 59 getR(v, u); sz[u] += sz[v]; gmax(mx[u], sz[v]); 60 } gmax(mx[u], size - sz[u]); 61 if (mx[R] > mx[u]) R = u; 62 } 63 void calc(int u, int f, int w, int c) { 64 gmax(ans, query(w, m - c)); 65 for (int v, i = 0; i < g[u].size(); ++i) 66 if ((v = g[u][i]) != f && !vis[v]) { 67 calc(v, u, w ^ val[v], c + a[v]); 68 } 69 } 70 void add(int u, int f, int w, int c) { 71 insert(w, c); 72 for (int v, i = 0; i < g[u].size(); ++i) 73 if ((v = g[u][i]) != f && !vis[v]) 74 add(v, u, w ^ val[v], c + a[v]); 75 } 76 void solve(int u) { 77 vis[u] = 1; C = mem; root = newNode(); insert(0, 0); 78 for (int v, i = 0; i < g[u].size(); ++i) if (!vis[v = g[u][i]]) { 79 calc(v, u, val[u] ^ val[v], a[u] + a[v]); 80 add(v, u, val[v], a[v]); 81 } gmax(ans, query(val[u], m - a[u])); 82 for (int v, i = 0; i < g[u].size(); ++i) if (!vis[v = g[u][i]]) { 83 size = sz[v]; getR(v, R = 0); solve(R); 84 } 85 } 86 int main() { 87 null->ch[0] = null->ch[1] = null; 88 ans = -1; mx[0] = ~0U >> 1; 89 read(n); read(m); 90 for (int i = 1, t; i <= n; ++i) 91 read(t), a[i] = t; 92 if (a.count() < m) return puts("-1"), 0; 93 for (int i = 1; i <= n; ++i) read(val[i]); 94 for (int i = 1, u, v; i < n; ++i) 95 read(u), read(v), ae(u, v); 96 size = n; getR(1, 1); solve(R); 97 printf("%d\n", ans); 98 return 0; 99 }
题意:有喜欢的点,点上有点权。求出一条路径使得点权异或和最大且有至少$k$个喜欢的点在路径上。
$k=0$时的原题:The xor-longest Path
要让异或和最大,当然还是在$trie$上贪心。
还是跟上面的套路一样,咱只统计经过了根节点的路径。
咱往$trie$里插入异或和时不计算根的值,算时把根加进去。这样就不会把根的异或掉。
在$trie$里还要额外记录路径上喜欢的节点的个数。