【Code Chef】April Challenge 2019
Subtree Removal
很显然不可能选择砍掉一对有祖先关系的子树。令$f_i$表示$i$子树的答案,如果$i$不被砍,那就是$a_i + \sum\limits_j f_j$;如果$i$被砍,那就是$-x$。取个$max$就好了。
时间、空间复杂度$O(n)$。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int tc, n, xx; int a[N]; vector<int> g[N]; long long f[N]; void Dfs(int x, int ft) { f[x] = a[x]; for (int i = 0; i < g[x].size(); ++i) { int v = g[x][i]; if (v == ft) continue; Dfs(v, x); f[x] += f[v]; } f[x] = max(f[x], -(long long)xx); } int main() { scanf("%d", &tc); for (; tc--; ) { scanf("%d%d", &n, &xx); for (int i = 1; i <= n; ++i) { scanf("%d", &a[i]); } for (int i = 1, x, y; i < n; ++i) { scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } Dfs(1, 0); printf("%lld\n", f[1]); // remember to clear up for (int i = 1; i <= n; ++i) { g[i].clear(); } } return 0; }
Playing with Numbers
在模$m$意义下,$a * k(k \in \mathbb{N})$能表示的最大的数就是$m - (a, m)$。容易推导出一个叶子的答案就是$m_i - (m, a_{b_1}, a_{b_2}, ... , a_{b_w})$,其中$b$表示$i$号点的祖先链。
时间复杂度$O(nlogn)$,空间复杂度$O(n)$。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int tc, n; vector<int> g[N]; long long a[N], m[N], gcd[N]; void Dfs(int x, int ft) { for (int i = 0; i < g[x].size(); ++i) { int v = g[x][i]; if (v == ft) continue; gcd[v] = __gcd(gcd[x], a[v]); Dfs(v, x); } } int main() { scanf("%d", &tc); for (; tc--; ) { scanf("%d", &n); for (int i = 1, x, y; i < n; ++i) { scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); } for (int i = 1; i <= n; ++i) { scanf("%lld", &m[i]); } gcd[1] = a[1]; Dfs(1, 0); for (int i = 2; i <= n; ++i) { if (g[i].size() == 1) { long long d = __gcd(gcd[i], m[i]); printf("%lld ", m[i] - d); } } printf("\n"); // remember to clear up for (int i = 1; i <= n; ++i) { g[i].clear(); } } return 0; }
Kira Loves Palindromes
令$f_{i,j}$表示以$i,j$为端点分别向左右两边扩展最长能匹配的长度。题目要求两个子串$s1, s2$拼起来是回文串的数量,我们假设其回文中心在$s2$里(在$s1$的话反过来在做一遍即可),我们枚举$s2$的左端点的位置$i$,在枚举回文中心的右端点$j$,则此时有方案数$\sum\limits_{k = 1}^{i - 1} f_{k, j + 1}$。对$f$做个前缀和,外面枚举个$i,j$即可。
时间、空间复杂度$O(n^2)$。
#include <bits/stdc++.h> using namespace std; typedef unsigned long long ULL; const int N = 1e3 + 5; const ULL BAS = 137; int n; char s[N]; int f[N][N], s1[N][N], s2[N][N]; long long ans; ULL H[N], hz[N], hv[N]; ULL Get(int l, int r, ULL *h) { return (h[r] - h[l - 1]) * H[N - l]; } bool Chkp(int l, int r) { if (l > r) return 1; int d = (r - l + 1) / 2; return Get(l, l + d - 1, hz) == Get(n - r + 1, n - r + d, hv); } int main() { H[0] = 1; for (int i = 1; i < N; ++i) { H[i] = H[i - 1] * BAS; } scanf("%s", s + 1); n = strlen(s + 1); for (int i = 1; i <= n; ++i) { hz[i] = hz[i - 1] + H[i] * s[i]; hv[i] = hv[i - 1] + H[i] * s[n - i + 1]; } for (int i = 1; i <= n; ++i) { for (int j = n; j > i; --j) { if (s[i] == s[j]) { f[i][j] = f[i - 1][j + 1] + 1; } } } for (int i = 1; i <= n; ++i) { for (int j = n; j > i; --j) { s1[i][j] = s1[i][j + 1] + f[i][j]; } } for (int j = 1; j <= n; ++j) { for (int i = 1; i < j; ++i) { s2[i][j] = s2[i - 1][j] + f[i][j]; } } for (int i = 2; i <= n; ++i) { for (int j = i; j <= n; ++j) { if (!Chkp(i, j - 1)) continue; ans += s2[i - 1][j]; } } for (int i = 1; i < n; ++i) { for (int j = 1; j < i; ++j) { if (!Chkp(j + 1, i)) continue; ans += s1[j][i + 1]; } } printf("%lld\n", ans); return 0; }
Offer for Chef
题中的那个运算就是位运算与,然后有用的位置只有$50$个(即如果$k$大于$t$中非$0$个数答案就是$0$)。可以考虑诸位确定,$check$时写个$f_{i,j}$表示把前$i$个数分成$j$段,每段和都是当前要$check$的数的母集即可。
时间复杂度$O(50^4)$,空间复杂度$O(n)$。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; const int M = 53; int n, nq, k; int t[N]; long long a[N]; set<long long> f[M][M]; int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); } scanf("%d", &nq); for (; nq--; ) { scanf("%d", &k); vector<long long> w, sw; long long tmp = 0; for (int i = 1; i <= n; ++i) { scanf("%d", &t[i]); if (t[i]) { w.push_back(a[i] * t[i]); tmp += a[i] * t[i]; sw.push_back(tmp); } } if (k > w.size()) { printf("0\n"); continue; } for (int i = 0; i < M; ++i) { for (int j = 0; j < M; ++j) { f[i][j].clear(); } } for (int i = 0; i < w.size(); ++i) { f[i][1].insert(sw[i]); } for (int i = 0; i < w.size(); ++i) { for (int j = 1; j < k; ++j) { for (int l = i + 1; l < w.size(); ++l) { for (long long e : f[i][j]) { f[l][j + 1].insert(e & (sw[l] - sw[i])); } } } } printf("%lld\n", *f[w.size() - 1][k].rbegin()); } return 0; }
Mininum XOR over Tree
不说了,直接$trie$树合并。
时间、空间复杂度$O(nlogn)$。
#include <bits/stdc++.h> using namespace std; const int T = 20; const int N = 2e5 + 5; const int M = N * T * 2; struct Node { int ch[2], idm; } node[M]; int tc, n, m, nq, tot; int w[N], rt[N]; vector<int> g[N]; int Chkmin(int x, int y) { if (!x || !y) return x | y; return min(x, y); } void Ins(int x) { rt[x] = ++tot; int p = rt[x]; for (int i = T - 1; ~i; --i) { int c = w[x] >> i & 1; if (!node[p].ch[c]) { node[p].ch[c] = ++tot; } p = node[p].ch[c]; } node[p].idm = x; } int Merge(int x, int y) { if (!x || !y) return x | y; int z = ++tot; node[z].ch[0] = Merge(node[x].ch[0], node[y].ch[0]); node[z].ch[1] = Merge(node[x].ch[1], node[y].ch[1]); node[z].idm = Chkmin(node[x].idm, node[y].idm); return z; } void Dfs(int x, int ft) { Ins(x); for (int i = 0; i < g[x].size(); ++i) { int v = g[x][i]; if (v == ft) continue; Dfs(v, x); rt[x] = Merge(rt[x], rt[v]); } } int main() { scanf("%d", &tc); for (; tc--; ) { scanf("%d%d", &n, &nq); for (int i = 1; i <= n; ++i) { scanf("%d", &w[i]); } for (int i = 1, x, y; i < n; ++i) { scanf("%d%d", &x, &y); g[x].push_back(y); g[y].push_back(x); } Dfs(1, 0); int lid = 0, lval = 0; for (int x, v; nq--; ) { scanf("%d%d", &x, &v); x ^= lid; v ^= lval; int p = rt[x], val = 0; for (int i = T - 1; ~i; --i) { int c = v >> i & 1; if (node[p].ch[c ^ 1]) { val += 1 << i; p = node[p].ch[c ^ 1]; } else { p = node[p].ch[c]; } } lid = node[p].idm; lval = val; printf("%d %d\n", lid, lval); } // remember to clear up for (int i = 1; i <= tot; ++i) { node[i].ch[0] = node[i].ch[1] = node[i].idm = 0; } tot = 0; for (int i = 1; i <= n; ++i) { g[i].clear(); rt[i] = 0; } } return 0; }
Edgy
考虑边的联通块个数就是$n - $所有出边颜色都相同的点的个数$+1$。我们只需要维护所有出边颜色都相同的点的个数即可。先轻重链剖分,令$nat_x$表示所有连向虚儿子的边的颜色种类(用$0$表示没有虚儿子,用$1,2$分别表示全都是$0/1$,用$3$表示$0/1$都有),然后每个点最多还剩下两条边(连向父亲的和连向重儿子的),只有这两条边的颜色和$nat$匹配才会算贡献。我们可以在树链上建线段树,维护翻转标记的同时维护区间内有贡献的点的个数。每次修改拆成两条到根的链,在线段树上执行区间翻转即可。注意,翻转轻边时候可能会修改该边上端点的$nat$值。
时间复杂度$O(nlog^2n)$,空间复杂度$O(n)$。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; int tc, n, nq, clk, ans; int fa[N], fw[N], si[N], so[N], tp[N]; int dfn[N], li[N], nat[N], cnt[N][2]; vector<pair<int, bool> > g[N]; bool Satis(int x, int a, int b) { if (a != b) return 0; if (!nat[x] || nat[x] == a + 1) return 1; return 0; } namespace S { const int M = N * 4; struct Node { int zl, zr, zsum, revsum, flp; } node[M]; vector<pair<Node, int> > qry; void Up(int t, int md) { Node &now = node[t]; Node &ls = node[t << 1]; Node &rs = node[t << 1 | 1]; now.zl = ls.zl; now.zr = rs.zr; now.zsum = ls.zsum + rs.zsum + Satis(li[md], ls.zr, rs.zl); now.revsum = ls.revsum + rs.revsum + Satis(li[md], ls.zr ^ 1, rs.zl ^ 1); } void Uflp(int t) { Node &now = node[t]; now.zl ^= 1; now.zr ^= 1; now.flp ^= 1; swap(now.zsum, now.revsum); } void Down(int t) { if (node[t].flp) { Uflp(t << 1); Uflp(t << 1 | 1); node[t].flp = 0; } } void Build(int t, int l, int r) { Node &now = node[t]; now.zsum = now.revsum = now.flp = 0; if (l == r) { now.zl = now.zr = fw[li[l]]; return; } int md = (l + r) >> 1; Build(t << 1, l, md); Build(t << 1 | 1, md + 1, r); Up(t, md); } void Flip(int t, int l, int r, int L, int R) { if (L <= l && r <= R) { Uflp(t); return; } int md = (l + r) >> 1; Down(t); if (L <= md) Flip(t << 1, l, md, L, R); if (R > md) Flip(t << 1 | 1, md + 1, r, L, R); Up(t, md); } void Modify(int t, int l, int r, int x) { if (l == r) return; int md = (l + r) >> 1; Down(t); if (x < md) Modify(t << 1, l, md, x); if (x > md) Modify(t << 1 | 1, md + 1, r, x); Up(t, md); } void Query(int t, int l, int r, int L, int R) { if (L <= l && r <= R) { qry.push_back(pair<Node, int>(node[t], r)); return; } int md = (l + r) >> 1; Down(t); if (L <= md) Query(t << 1, l, md, L, R); if (R > md) Query(t << 1 | 1, md + 1, r, L, R); } int Ask(int t, int l, int r, int x) { if (l == r) return node[t].zl; int md = (l + r) >> 1; Down(t); if (x <= md) return Ask(t << 1, l, md, x); return Ask(t << 1 | 1, md + 1, r, x); } int Query(int L, int R) { if (L > R) return 0; qry.clear(); Query(1, 1, n, L, R); int ret = 0; for (int i = 0; i < qry.size(); ++i) { ret += qry[i].first.zsum; if (i) { ret += Satis(li[qry[i - 1].second], qry[i - 1].first.zr, qry[i].first.zl); } } return ret; } } bool Chk(int x) { if (x == 1) { int ws = S::Ask(1, 1, n, dfn[so[x]]); return Satis(x, ws, ws); } if (!so[x]) { int w = S::Ask(1, 1, n, dfn[x]); return Satis(x, w, w); } else { int w = S::Ask(1, 1, n, dfn[x]); int ws = S::Ask(1, 1, n, dfn[so[x]]); return Satis(x, ws, w); } } void Wish(int x) { if (cnt[x][0] && cnt[x][1]) { nat[x] = 3; } else if (cnt[x][0]) { nat[x] = 1; } else if (cnt[x][1]) { nat[x] = 2; } } void Dfs0(int x) { si[x] = 1; for (int i = 0; i < g[x].size(); ++i) { int v = g[x][i].first, w = g[x][i].second; if (v == fa[x]) continue; fa[v] = x; fw[v] = w; Dfs0(v); si[x] += si[v]; if (si[v] > si[so[x]]) so[x] = v; } } void Dfs1(int x, int gr) { tp[x] = gr; dfn[x] = ++clk; li[clk] = x; if (so[x]) Dfs1(so[x], gr); for (int i = 0; i < g[x].size(); ++i) { int v = g[x][i].first; if (v != fa[x] && v != so[x]) { Dfs1(v, v); ++cnt[x][g[x][i].second]; } } Wish(x); } void Flip(int u) { for (int x = u; x; x = fa[tp[x]]) { ans -= S::Query(dfn[tp[x]] + 1, dfn[x]); ans -= Chk(tp[x]) + (tp[x] != x? Chk(x) : 0); } for (int x = u; x; x = fa[tp[x]]) { int z = tp[x]; int y = fa[z]; if (y) { int w = S::Ask(1, 1, n, dfn[z]); --cnt[y][w]; ++cnt[y][w ^ 1]; Wish(y); S::Modify(1, 1, n, dfn[y]); } S::Flip(1, 1, n, dfn[tp[x]], dfn[x]); } for (int x = u; x; x = fa[tp[x]]) { ans += S::Query(dfn[tp[x]] + 1, dfn[x]); ans += Chk(tp[x]) + (tp[x] != x? Chk(x) : 0); } } int main() { scanf("%d", &tc); for (; tc--; ) { scanf("%d", &n); for (int i = 1, x, y, z; i < n; ++i) { scanf("%d%d%d", &x, &y, &z); g[x].push_back(pair<int, bool>(y, z)); g[y].push_back(pair<int, bool>(x, z)); } Dfs0(1); Dfs1(1, 1); S::Build(1, 1, n); ans = 0; for (int i = 1; i <= n; ++i) { if (!so[i]) { ++ans; if (tp[i] != i) { ans += Chk(tp[i]); ans += S::Query(dfn[tp[i]] + 1, dfn[i]); } } } scanf("%d", &nq); for (int u, v; nq--; ) { scanf("%d%d", &u, &v); Flip(u); Flip(v); printf("%d\n", (n == 1)? 0 : n - ans + 1); } // remember to clear up clk = 0; for (int i = 1; i <= n; ++i) { g[i].clear(); so[i] = tp[i] = nat[i] = cnt[i][0] = cnt[i][1] = 0; } } return 0; }
Sonya and Queries
(被象飞了,写了4个小时)
我们暂且不考虑操作$7$。如果只有前六个操作,大概就是要实现一个数据结构,支持:1,在原树的基础上$link/cut$;2,整个联通块加上一个正数;3,整个联通块赋值为$0$;4,联通块求和;5,单点加,单点查询。由于只有子树的相关操作,所以这些都可以用$ETT$直接解决,也十分好写,只要实现一棵平衡树维护双括号序,$link/cut$时只要$merge/split$就行了。
操作$7$,其要求所有有值的联通块构成的虚树的边数(也就是点数)。考虑如果我们现在有一棵以联通块为点构成的树$T'$,记$cnt_x$表示$x$的子树里有值的点的个数,令$tot$为$T'$点的个数,则要求的虚树的点数为:$tot - \sum\limits_{i = 1}^{tot}[cnt_i = 0] - \sum\limits_{i = 1}^{tot}[cnt_i = cnt_{root}] + 1$。如果某一个点是$0$是$1$的状态改变了,相当于对该点的祖先链的$cnt$执行$+1$或$-1$,实现起来简单点的话可以直接轻重链剖分,用线段树维护树链,并且记录区间内最大值及其个数,最小值及其个数,$\sum\limits_{i = 1}^{tot}[cnt_i = 0]$就是全局最小值的个数(如果最小值是$0$的话),$\sum\limits_{i = 1}^{tot}[cnt_i = cnt_{root}]$就是全局最大值的个数。回到原树上,我们把每个联通块的贡献算到该联通块的根上,即只用其根来标志这个联通块,每个$1$~$6$操作后可能会改掉某个联通块是否有值的状态,也可能会增加或取消某个用于标记联通块的点,这些都扔到线段树上改改就行了。
时间复杂度$O(nlog^2n)$,空间复杂度$O(n)$。
#include <bits/stdc++.h> using namespace std; const int N = 2.5e5 + 5; const int INF = 1e9 + 7; const pair<int, int> PINF = pair<int, int>(INF, 0); const pair<int, int> PFNI = pair<int, int>(-INF, 0); int n, nq, tot, clk; int tv[N * 2], bel[N], fw[N], valsub[N], ival[N]; vector<int> g[N]; int fa[N], si[N], so[N], tp[N], dep[N], dfn[N], li[N]; bool usd[N]; char av[N]; namespace S { struct Node { pair<int, int> mn, mx; int tagadd, cnt; } node[N * 4]; pair<int, int> Comb_min(pair<int, int> a, pair<int, int> b) { if (a.first < b.first) { return a; } else if (a.first > b.first) { return b; } else { return pair<int, int>(a.first, a.second + b.second); } } pair<int, int> Comb_max(pair<int, int> a, pair<int, int> b) { if (a.first > b.first) { return a; } else if (a.first < b.first) { return b; } else { return pair<int, int>(a.first, a.second + b.second); } } void Up(int t) { node[t].mn = Comb_min(node[t << 1].mn, node[t << 1 | 1].mn); node[t].mx = Comb_max(node[t << 1].mx, node[t << 1 | 1].mx); } void Uadd(int t, int _v) { Node &nw = node[t]; nw.tagadd += _v; nw.cnt += _v; nw.mn.first += _v; nw.mx.first += _v; } void Down(int t) { if (node[t].tagadd) { Uadd(t << 1, node[t].tagadd); Uadd(t << 1 | 1, node[t].tagadd); node[t].tagadd = 0; } } void Build(int t, int l, int r) { if (l == r) { node[t].cnt = valsub[li[l]]; node[t].mn = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PINF; node[t].mx = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PFNI; return; } int md = (l + r) >> 1; Build(t << 1, l, md); Build(t << 1 | 1, md + 1, r); Up(t); } void Modify(int t, int l, int r, int L, int R, int _v) { if (L <= l && r <= R) { Uadd(t, _v); return; } int md = (l + r) >> 1; Down(t); if (L <= md) Modify(t << 1, l, md, L, R, _v); if (R > md) Modify(t << 1 | 1, md + 1, r, L, R, _v); Up(t); } void Change_usd(int t, int l, int r, int x) { if (l == r) { node[t].mn = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PINF; node[t].mx = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PFNI; return; } int md = (l + r) >> 1; Down(t); if (x <= md) Change_usd(t << 1, l, md, x); else Change_usd(t << 1 | 1, md + 1, r, x); Up(t); } pair<int, int> Query_min(int t, int l, int r, int L, int R) { if (L <= l && r <= R) { return node[t].mn; } int md = (l + r) >> 1; Down(t); pair<int, int> re = PINF; if (L <= md) re = Comb_min(re, Query_min(t << 1, l, md, L, R)); if (R > md) re = Comb_min(re, Query_min(t << 1 | 1, md + 1, r, L, R)); return re; } pair<int, int> Query_max(int t, int l, int r, int L, int R) { if (L <= l && r <= R) { return node[t].mx; } int md = (l + r) >> 1; Down(t); pair<int, int> re = PFNI; if (L <= md) re = Comb_max(re, Query_max(t << 1, l, md, L, R)); if (R > md) re = Comb_max(re, Query_max(t << 1 | 1, md + 1, r, L, R)); return re; } } int li2[N * 2], st[N * 2], ed[N * 2], clk2; namespace T { struct Node { int lc, rc, p, sz, rnd, tagcov; long long val, sum, tagadd; } node[N * 2]; void Up(int t) { node[t].sum = node[t].val + node[node[t].lc].sum + node[node[t].rc].sum; node[t].sz = 1 + node[node[t].lc].sz + node[node[t].rc].sz; } void Ucov(int t) { Node &nw = node[t]; nw.val = nw.sum = nw.tagadd = 0; nw.tagcov = 1; } void Uadd(int t, long long _v) { Node &nw = node[t]; nw.val += _v; nw.tagadd += _v; nw.sum += _v * nw.sz; } void Down(int t) { Node &nw = node[t]; if (nw.tagcov) { if (nw.lc) Ucov(nw.lc); if (nw.rc) Ucov(nw.rc); nw.tagcov = 0; } if (nw.tagadd) { if (nw.lc) Uadd(nw.lc, nw.tagadd); if (nw.rc) Uadd(nw.rc, nw.tagadd); nw.tagadd = 0; } } void Split(int t, int k, int &x, int &y) { if (!k) { x = 0; y = t; return; } Down(t); if (k <= node[node[t].lc].sz) { y = t; node[node[t].lc].p = 0; Split(node[t].lc, k, x, node[t].lc); node[node[t].lc].p = t; } else { x = t; node[node[t].rc].p = 0; Split(node[t].rc, k - node[node[t].lc].sz - 1, node[t].rc, y); node[node[t].rc].p = t; } Up(t); } int Merge(int x, int y) { if (!x || !y) { return x | y; } if (node[x].rnd < node[y].rnd) { Down(x); node[x].rc = Merge(node[x].rc, y); node[node[x].rc].p = x; Up(x); return x; } else { Down(y); node[y].lc = Merge(x, node[y].lc); node[node[y].lc].p = y; Up(y); return y; } } int Rank(int x) { int ret = node[node[x].lc].sz; while (node[x].p) { if (x == node[node[x].p].rc) { x = node[x].p; ret += node[node[x].lc].sz + 1; } else { x = node[x].p; } } return ret + 1; } void Roll(int x) { if (node[x].p) { Roll(node[x].p); } Down(x); } void Wish(int x, long long _v) { Roll(x); node[x].val = _v; while (x) { Up(x); x = node[x].p; } } long long Thanks(int x) { Roll(x); return node[x].val; } int Find_root(int x) { while (node[x].p) { x = node[x].p; } return x; } int Gen(int x) { while (node[x].lc) { x = node[x].lc; } return x; } } void Dfs0(int x) { si[x] = 1; valsub[x] = ival[x] > 0; for (int i = 0; i < g[x].size(); ++i) { int e = g[x][i], v = tv[e]; if (v == fa[x]) continue; fa[v] = x; dep[v] = dep[x] + 1; bel[e / 2] = v; fw[v] = e / 2; Dfs0(v); si[x] += si[v]; if (si[v] > si[so[x]]) so[x] = v; valsub[x] += valsub[v]; } } void Dfs1(int x, int gr) { dfn[x] = ++clk; li[clk] = x; st[x] = ++clk2; li2[clk2] = x; T::node[clk2] = (T::Node){ 0, 0, 0, 1, rand(), 0, ival[x], ival[x], 0 }; tp[x] = gr; if (so[x]) { Dfs1(so[x], gr); } for (int i = 0; i < g[x].size(); ++i) { int e = g[x][i], v = tv[e]; if (v != fa[x] && v != so[x]) { Dfs1(v, v); } } ed[x] = ++clk2; li2[clk2] = x; T::node[clk2] = (T::Node){ 0, 0, 0, 1, rand(), 0, ival[x], ival[x], 0 }; T::Merge(st[x], ed[x]); } void Chain_update(int x, int _v) { while (x) { S::Modify(1, 1, n, dfn[tp[x]], dfn[x], _v); x = fa[tp[x]]; } } void Cut_from_father(int x) { int l = st[x], r = ed[x]; int rkl = T::Rank(l), rkr = T::Rank(r); int rt = T::Find_root(l); long long lsv = T::node[rt].sum; static int a, b, c; T::Split(rt, rkr, b, c); T::Split(b, rkl - 1, a, b); rt = T::Merge(a, c); usd[x] = 1; ++tot; S::Change_usd(1, 1, n, dfn[x]); if (T::node[b].sum > 0) { Chain_update(x, 1); } if (lsv > 0 && T::node[rt].sum == 0) { int ge = li2[T::Gen(rt)]; Chain_update(ge, -1); } } void Link_to_father(int x) { int y = fa[x]; int rkl = T::Rank(st[y]); int rt = T::Find_root(st[y]); long long lsv = T::node[rt].sum; static int a, b, c; T::Split(rt, rkl, a, c); b = T::Find_root(st[x]); if (T::node[b].sum > 0) { Chain_update(x, -1); } usd[x] = 0; --tot; S::Change_usd(1, 1, n, dfn[x]); rt = T::Merge(T::Merge(a, b), c); if (lsv == 0 && T::node[rt].sum > 0) { int ge = li2[T::Gen(rt)]; Chain_update(ge, 1); } } int main() { scanf("%*d%d%d", &n, &nq); tot = n; for (int i = 1, x, y; i < n; ++i) { scanf("%d%d", &x, &y); tv[i * 2] = y; g[x].push_back(i * 2); tv[i * 2 + 1] = x; g[y].push_back(i * 2 + 1); } scanf("%s", av + 1); for (int i = 1; i <= n; ++i) { scanf("%d", &ival[i]); usd[i] = 1; } Dfs0(1); Dfs1(1, 1); S::Build(1, 1, n); for (int i = 1; i < n; ++i) { av[i] -= '0'; if (av[i] == 0) { Link_to_father(bel[i]); } } for (int ty, x, y; nq--; ) { scanf("%d", &ty); if (ty != 7) { scanf("%d", &x); } if (ty == 1) { av[x] ^= 1; if (av[x] == 1) { Cut_from_father(bel[x]); } else { Link_to_father(bel[x]); } } if (ty == 2) { scanf("%d", &y); if (y != 0) { int rt = T::Find_root(st[x]); long long lsv = T::node[rt].sum; T::Uadd(rt, y); if (!lsv) { int ge = li2[T::Gen(rt)]; Chain_update(ge, 1); } } } if (ty == 3) { int rt = T::Find_root(st[x]); long long sum = T::node[rt].sum / 2; T::Ucov(rt); T::Wish(st[x], sum); T::Wish(ed[x], sum); } if (ty == 4) { printf("%lld\n", T::Thanks(st[x])); } if (ty == 5) { int rt = T::Find_root(st[x]); long long sum = T::node[rt].sum; printf("%lld\n", sum / 2); } if (ty == 6) { int rt = T::Find_root(st[x]); long long lsv = T::node[rt].sum; T::Ucov(rt); if (lsv) { int ge = li2[T::Gen(rt)]; Chain_update(ge, -1); } } if (ty == 7) { int res = tot; res -= S::Query_max(1, 1, n, 1, n).second - 1; pair<int, int> mim = S::Query_min(1, 1, n, 1, n); if (mim.first == 0) { res -= mim.second; } printf("%d\n", max(0, res - 1)); } if (ty == 4 || ty == 5 || ty== 7) { fflush(stdout); } } return 0; }