1. 程式人生 > >dsu on tree

dsu on tree

pen try trie樹 奇數 ans play upd music isa

osu on tree?

dsu on tree!

這種操作可以在O(nlogn)的時間內解決一些無修改子樹詢問問題。

咱知道把一棵樹輕重鏈剖分後,樹的輕鏈,重鏈都只有O(logn)個。

這個算法就是利用了這一點,遞歸處理時保留重兒子的信息,輕兒子的則重新計算。

乍一看感覺很暴力,但是實際上是O(nlogn)的。

來看幾道題吧。

CodeForces - 600E

技術分享圖片
 1 #include<cstdio>
 2 #include<vector>
 3 #include<bitset>
 4 #include<iostream>
 5 #define
pb push_back 6 #define nc getchar 7 using namespace std; 8 inline void read(int &x) { 9 char b = nc(); x = 0; 10 for (; !isdigit(b); b = nc()); 11 for (; isdigit(b); b = nc()) x = x * 10 + b - 0; 12 } 13 vector < int > g[100005]; 14 inline void aE(int u, int v) { 15 g[u].pb(v); g[v].pb(u);
16 } 17 int n, rt, c[100005], sz[100005], mx, cnt[100005], son[100005]; 18 long long ans[100005], tans; 19 bitset < 100005 > h; 20 void dfz(int u, int f) { 21 sz[u] = 1; for (int v, i = 0; i < g[u].size(); ++i) if ((v = g[u][i]) != f) { 22 dfz(v, u), sz[u] += sz[v]; if (sz[v] > sz[son[u]]) son[u] = v;
23 } 24 } 25 void add(int u, int f) { 26 if (++cnt[c[u]] > mx) mx = cnt[c[u]], tans = c[u]; 27 else if (cnt[c[u]] == mx) tans += c[u]; 28 for (int v, i = 0; i < g[u].size(); ++i) 29 if ((v = g[u][i]) != f && !h[v]) add(v, u); 30 } 31 void del(int u, int f) { 32 --cnt[c[u]]; 33 for (int v, i = 0; i < g[u].size(); ++i) 34 if ((v = g[u][i]) != f && !h[v]) del(v, u); 35 } 36 void dfs(int u, int f, bool k) { 37 int s = son[u]; 38 for (int v, i = 0; i < g[u].size(); ++i) 39 if ((v = g[u][i]) != f && v != s) dfs(v, u, 0); 40 if (s) dfs(s, u, 1), h[s] = 1; 41 add(u, f); ans[u] = tans; 42 if (s) h[s] = 0; 43 if (!k) del(u, f), mx = tans = 0; 44 } 45 int main() { 46 read(n); 47 for (int i = 1; i <= n; ++i) read(c[i]); 48 for (int u, v, i = 1; i < n; ++i) 49 read(u), read(v), aE(u, v); 50 dfz(1, 1); dfs(1, 1, 0); 51 for (int i = 1; i <= n; ++i) printf("%lld ", ans[i]); 52 return 0; 53 }
View Code

把小的往大的上面合並,有絲按秩合並並查集的味道。

CodeForces - 570D

技術分享圖片
 1 #include<vector>
 2 #include<cstdio>
 3 #include<bitset>
 4 #include<iostream>
 5 using namespace std;
 6 inline char nc() {
 7     static char b[1<<20],*s=b,*t=b;
 8     return s==t&&(t=(s=b)+fread(b,1,1<<20,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 inline void read(char &x) {
16     for (x = nc(); !isalpha(x); x = nc());    
17 }
18 const int N = 500010;
19 int n, m, dep[N], sz[N], son[N];
20 char s[N];
21 vector < int > g[N];
22 struct Node {int h, id;};
23 vector < Node > q[N];
24 bool T[N][26];
25 bitset < N > ans, o;
26 void dfs(int u) {
27     sz[u] = 1;
28     for (int i = 0, v; i < g[u].size(); ++i) {
29         v = g[u][i]; dep[v] = dep[u] + 1; dfs(v);
30         sz[u] += sz[v]; if (sz[son[u]] < sz[v]) son[u] = v;
31     }
32 }
33 void acc(int u) {
34     T[dep[u]][s[u]] ^= 1;
35     for (int i = 0; i < g[u].size(); ++i)
36         if (!o[g[u][i]]) acc(g[u][i]);
37 }
38 inline bool check(int d) {
39     int cnt = 0;
40     for (int i = 0; i < 26; ++i)
41         cnt += T[d][i];
42     return cnt <= 1;
43 }
44 void dfs(int u, bool k) {
45     int s = son[u];
46     for (int i = 0; i < g[u].size(); ++i)
47         if (g[u][i] != s) dfs(g[u][i], 0);
48     if (s) dfs(s, 1), o[s] = 1; acc(u);
49     for (int i = 0; i < q[u].size(); ++i)
50         ans[q[u][i].id] = check(q[u][i].h);
51     if (s) o[s] = 0; if (!k) acc(u);
52 }
53 int main() {
54     read(n); read(m); dep[1] = 1;
55     for (int v = 2, u; v <= n; ++v)
56         read(u), g[u].push_back(v);
57     for (int i = 1; i <= n; ++i) read(s[i]), s[i] -= a;
58     dfs(1);
59     for (int h, u, i = 0; i < m; ++i) {
60         read(u), read(h); q[u].push_back((Node){h, i});
61     } dfs(1, 0);
62     for (int i = 0; i < m; ++i) puts(ans[i] ? "Yes" : "No");
63     return 0;
64 }
View Code

如果要能構成回文的話,那麽出現奇數次字符的個數需小於2。

在查詢節點處拉一個鏈表,dfs遞歸處理。

現在需要解決的問題就是如何快速回答以x為根的子樹中高度為h的字符出現的次數。

很顯然可以用可持久化trie樹解決這個問題,不過這不是這裏的重點。

還是向上面一樣,保留重兒子的貢獻,輕兒子的貢獻再算一遍。

CodeForces - 741D

技術分享圖片
 1 #include<cstring>
 2 #include<bitset>
 3 #include<vector>
 4 #include<cstdio>
 5 #include<iostream>
 6 #define pb push_back
 7 using namespace std;
 8 inline char nc() {
 9     static char b[1<<14],*s=b,*t=b;
10     return s==t&&(t=(s=b)+fread(b,1,1<<14,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 inline void readc(int &b) {
18     for (b = nc(); !isalpha(b); b = nc());
19 }
20 const int N = 500010;
21 int n, dep[N], sz[N], son[N], d[N], f[1<<22], ans[N], inf;
22 vector < int > g[N];
23 bitset < N > o;
24 inline void gmax(int &x, int y) {
25     if (x < y) x = y;
26 }
27 void dfs(int u) {
28     sz[u] = 1;
29     for (int i = 0, v; i < g[u].size(); ++i) {
30         v = g[u][i]; dep[v] = dep[u] + 1; d[v] ^= d[u];
31         dfs(v); sz[u] += sz[v];
32         if (sz[son[u]] < sz[v]) son[u] = v;
33     }
34 }
35 void add(int u) {
36     gmax(f[d[u]], dep[u]);
37     for (int i = 0; i < g[u].size(); ++i)
38         if (!o[g[u][i]]) add(g[u][i]);
39 }
40 void del(int u) {
41     f[d[u]] = inf;
42     for (int i = 0; i < g[u].size(); ++i)
43         if (!o[g[u][i]]) del(g[u][i]);
44 }
45 inline void upd(int u, int &res) {
46     gmax(res, dep[u] + f[d[u]]);
47     for (int i = 0; i < 22; ++i)
48         gmax(res, f[(1<<i)^d[u]] + dep[u]);
49 }
50 void calc(int u, int &res) {
51     upd(u, res); for (int i = 0; i < g[u].size(); ++i)
52         if (!o[g[u][i]]) calc(g[u][i], res);
53 }
54 void dfs(int u, bool k) {
55     int s = son[u], res = 0;
56     for (int i = 0; i < g[u].size(); ++i)
57         if (g[u][i] != s) dfs(g[u][i], 0);
58     if (s) dfs(s, 1), o[s] = 1;
59     for (int v, i = 0; i < g[u].size(); ++i)
60         if ((v = g[u][i]) != s) calc(v, res), add(v);
61     gmax(f[d[u]], dep[u]); upd(u, res);
62     ans[u] = res - 2 * dep[u];
63     for (int i = 0; i < g[u].size(); ++i)
64         gmax(ans[u], ans[g[u][i]]);
65     if (s) o[s] = 0; if (!k) del(u);
66 }
67 void upd(int u) {
68     for (int i = 0; i < g[u].size(); ++i)
69         upd(g[u][i]), gmax(ans[u], ans[g[u][i]]);
70 }
71 int main() {
72     read(n); memset(f, 128, sizeof(f)); inf = f[0];
73     for (int i = 2, b, t; i <= n; ++i)
74         read(t), readc(b), g[t].pb(i), d[i] |= (1 << (b - a));
75     dfs(1); dfs(1, 0);
76     for (int i = 1; i <= n; ++i) printf("%d ", ans[i]);
77     return 0;
78 }
View Code

這道題一開始咱錯的原因很搞笑,add和del函數裏的add與del我寫順手寫成dfs了。

把字符都當成二進制上的某一位。預處理根節點到每一點的異或和f(1,i)。

由異或的性質可知,f(u,v) = f(1,u) xor f(1,v)。

所以如果u-v這條路徑上的字符可以構成回文,那麽f(u,v)上至多有1位為1,即f(u,v)==0或1<<i。

咱只需要在以x為根的子樹中找出u,v滿足f(u,v)==0或1<<i,且使得dep[u]+dep[v]-2*dep[lca(u,v)]最大。

但是lca(u,v)不一定是x,這就會造成咱用dep[u]+dep[v]-2*dep[x]所計算出的答案大了。

所以咱要操作一番,讓u,v在x的不同子樹中。

這個好搞喲,咱只要先統計一棵子樹再去更新它就行。

ref:

[Tutorial] Sack (dsu on tree)

樹上啟發式合並 (dsu on tree)

[trick]dsu on tree

【學習筆記】dsu on tree

[dsu on tree]【學習筆記】

白金ディスコ

dsu on tree