我曾在弦歌之中听过你,
檀板声碎,半出折子戏。
舞榭歌台被风吹去,
岁月深处尚有余音一缕……
Gty神(xian)犇(chong)从来不缺妹子……
他来到了一棵妹子树下,发现每个妹子有一个美丽度……
由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣。
他想知道某个子树中美丽度大于k的妹子个数。
某个妹子的美丽度可能发生变化……
树上可能会出现一只新的妹子……
维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi。
支持以下操作:
0 u x 询问以u为根的子树中,严格大于x的值的个数。(u^=lastans,x^=lastans)
1 u x 把u节点的权值改成x。(u^=lastans,x^=lastans)
2 u x 添加一个编号为”当前树中节点数+1”的节点,其父节点为u,其权值为x。(u^=lastans,x^=lastans)
最开始时lastans=0。
输入第一行包括一个正整数n(1<=n<=30000),代表树上的初始节点数。
接下来n-1行,每行2个整数u,v,为树上的一条无向边。
任何时刻,树上的任何权值大于等于0,且两两不同。
接下来1行,包括n个整数wi,表示初始时每个节点的权值。
接下来1行,包括1个整数m(1<=m<=30000),表示操作总数。
接下来m行,每行包括三个整数 op,u,v:
op,u,v的含义见题目描述。
保证题目涉及的所有数在int内。
对每个op=0,输出一行,包括一个整数,意义见题目描述。
2
1 2
10 20
1
0 1 5
2
HINT
2017.9.28新加数据一组by GXZlegend,未重测
Source
By Autumn
询问修改分块的神仙题。
或者也可以称他为按时间分块。
隔 k k 次重构,否则暴力算修改对询问的影响。
这样题目被划分为重构和修改两个部分。
对于重构,可以考虑没有修改的情况。
可以dfs序+主席树搞。dfs序可以把子树变成一段区间,主席树询问区间第 k k 大。
这样的话,每次重构重新做一遍就好了。
复杂度 O(qknlogn+qlogn) O ( q k n l o g n + q l o g n )
对于修改,有两种操作。
首先考虑2操作。记录下这个新加入节点在哪个已经重构过的深度最大的节点的子树内,记录这个节点为 g g 。
这个可以用类似并查集的方法维护。
询问的时候分类讨论。如果询问到新加入的节点,其当前子树大小不会超过 k k ,于是可暴力搜索。
否则的话先用重构的信息,再暴力判断新加入节点的 g g 是否在当前子树内即可。
复杂度是 O(kq) O ( k q )
对于修改权值的操作,如果是新加入的节点就不管,否则的话重构过的主席树部分会错误计算,记录修改前修改该后,询问的时候还是判断是否在子树内就好,暴力记录贡献就好了。
复杂度还是 O(kq) O ( k q )
总的复杂度 O(kq+qknlogn+qlogn) O ( k q + q k n l o g n + q l o g n )
取 k=nlogn−−−−−√ k = n l o g n ,复杂度就是 O(qnlogn−−−−−√) O ( q n l o g n )
其实这题有一个很不稳的分块做法,可以写一个假的块状树,但是会被菊花图卡掉。
还有一个神仙的平衡树套平衡树做法,本蒟蒻不会。
#include
#include
const int T = 2e6 + 10, N = 1e5 + 10;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int s[T], ls[T], rs[T], rt[N], g[N], a[N], to[N], nx[N], x[N], n, sz;
int in[N], out[N], b[N], w[N], p[N], nw[N], pr[N], nn, Z, r, tot, tp, C;
void add(int u, int v) {to[++C] = v; nx[C] = pr[u]; pr[u] = C;}
void adds(int u, int v) {add(u, v); add(v, u);}
int F(int x) {
int L = 1, R = nn, m;
for(;L != R; b[m = L + R >> 1] >= x ? R = m : L = m + 1) ;
return L;
}
void Ins(int &cp, int np, int L, int R, int v) {
s[cp = ++Z] = s[np] + 1;
if(L == R) return ; int m = L + R >> 1;
if(v <= m) Ins(ls[cp], ls[np], L, m, v), rs[cp] = rs[np];
else Ins(rs[cp], rs[np], m + 1, R, v), ls[cp] = ls[np];
}
void Que(int lt, int rt, int L, int R, int k) {
if(L == k) return void(r += s[rt] - s[lt]);
int m = L + R >> 1;
if(k <= m) Que(ls[lt], ls[rt], L, m, k), r += s[rs[rt]] - s[rs[lt]];
else Que(rs[lt], rs[rt], m + 1, R, k);
}
void Dfs(int u, int f) {
in[u] = ++tot; p[tot] = u; g[u] = 0;
for(int i = pr[u]; i; i = nx[i]) if(to[i] != f) Dfs(to[i], u);
out[u] = tot;
}
void Work(int u, int k) {
w[u] >= k ? ++r : 0;
for(int i = pr[u]; i; i = nx[i]) Work(to[i], k);
}
void Build() {
Z = 0; n = sz;
for(int i = 1;i <= n; ++i) b[i] = w[i];
std::sort(b + 1, b + n + 1); b[nn = 1] = b[1];
for(int i = 2;i <= n; ++i) if(b[i] != b[i - 1]) b[++nn] = b[i];
tot = 0; Dfs(1, 0);
for(int i = 1;i <= n; ++i) Ins(rt[i], rt[i - 1], 1, nn, F(w[p[i]]));
}
int Que(int u, int v) {
++v; r = 0;
if(u > n) return Work(u, v), r;
if(v <= b[nn]) Que(rt[in[u] - 1], rt[out[u]], 1, nn, F(v));
for(int i = n + 1; i <= sz; ++i)
if(in[u] <= in[g[i]] && in[g[i]] <= out[u] && w[i] >= v) ++r;
for(int i = 1; i <= tp; ++i)
if(in[u] <= in[a[i]] && in[a[i]] <= out[u])
x[i] >= v ? --r : 0, nw[i] >= v ? ++r : 0;
return r;
}
int main() {
sz = n = ri();
for(int i = 1;i < n; ++i) adds(ri(), ri());
for(int i = 1;i <= n; ++i) w[i] = ri();
Build();
for(int m = ri(), la = 0, C = 0, pr = 0, M = 547; m--;) {
int op = ri(), u = ri() ^ la, x = ri() ^ la;
!op ? printf("%d\n", la = Que(u, x)) : ++C;
if(op == 1) u > n ? w[u] = x : (a[++tp] = u, ::x[tp] = w[u], nw[tp] = w[u] = x);
if(op == 2) add(u, ++sz), g[sz] = (u <= n ? u : g[u]), w[sz] = x;
if(C - pr >= M) Build(), pr = C, tp = 0;
}
return 0;
}