我觉得很厉害。要是考场上能把这道题切了的话数据结构的水平肯定是不低的。
考虑简化版问题:如果只询问一个点的答案怎么做。
注意,我这么做是有风险的。我把战线拉长了。不过当然,如果连简化版的问题都做不了,那何谈正解?幸运的是,这确实是一道数据结构多合一的题。
考虑 长链剖分 。那么在 x x x节点上加入棋子时,子树外的点就异或上 x x x子树的最大深度,如果以 x x x为根的最长链在重儿子上面,那么就给除了重儿子外的子树打标记,注意到 其 dfn \text{dfn} dfn序是连续的 ,可以直接打标;对于重儿子也可以直接对整颗树打标,只需处理出 x x x去掉重儿子后的最长链长度即可;如果最长链在父亲上,那么直接对 x x x整颗子树修改即可。
发现了吗?经过细致分析,我们发现这道题其实并不复杂。当然要建立在想到长链剖分的基础上。
这题更神奇的地方在于,让我们求
dist(x,v)
≤
1
\text{dist(x,v)}\le 1
dist(x,v)≤1的所有根的答案。这是个非常恼人的限制,因为你知道会被菊花图卡,但是不知道会被卡成多少分。
有没有严格的做法呢?答案是有的。但是我一定想不到。 但是需要用到非常高级的技巧。其实说白了,询问可以拆分成
x
x
x,
x
x
x的父亲,
x
x
x的重儿子以及
x
x
x的所有轻儿子。如果一次插入影响到点的数目是
O
(
1
)
O(1)
O(1)那么我们的目的就达到了。
考虑这样一个问题,如何用字典树维护
c
i
⊕
x
>
d
i
c_i\oplus x>d_i
ci⊕x>di的所有点对?这个问题也非常具有迷惑性。因为
d
i
d_i
di是定值,
x
x
x又是每次询问给定的,那么维护
c
i
c_i
ci然后在
trie
\text{trie}
trie树上查不就完了?并且
trie
\text{trie}
trie树查询的复杂度也是
log
n
\log n
logn的。这样分析下来觉得越来越有道理了,但是考场上完全想不到这里来啊???
初始化的时候 c i c_i ci都是定值。手动分讨一波,如果最长链在重儿子上面那么 x x x的所有轻儿子都异或上同一个数,直接在 x x x上打标就完了;如果最长链在父亲上面那么轻儿子还是异或上同一个数。事实上可以发现,任意时刻轻儿子的标都和这个点上的标是一样的,唯一的例外是当插入的点就是这个轻儿子的情况。但是正如前所说,修改影响到的节点数目是 O ( 1 ) O(1) O(1)的,所以直接在 trie \text{trie} trie树上暴力修改就行。然后就做完了。
所以发现了吗?这种题逻辑链条太长了。其实思维难度并不大。
复杂度 O ( n log n ) O(n\log n) O(nlogn)。
要考虑的细节挺多了。所以代码先咕了。
代码写错了好多地方,所以调了好久,看来我还是太菜了。。。
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
const int N=1e6+5;
int type;
int n,m,sz[N],dp[N],dp2[N],dpfa[N],son[N],bitxor[N],dfn[N],num;
int trie[N*25][2],tot,rt[N],fa[N],sztree[N*25],c[N];
vector<int>G[N];
void dfs(int u,int topf){
sz[u]=1,fa[u]=topf;
for(auto v:G[u]){
if(v!=topf){
dfs(v,u),sz[u]+=sz[v];
if(!son[u]||dp[v]>dp[son[u]]){
son[u]=v;
}
if(dp[v]+1>dp[u])dp2[u]=dp[u],dp[u]=dp[v]+1;
else if(dp[v]+1>dp2[u])dp2[u]=dp[v]+1;
}
}
}
//fixed
void ins(int it,int val,int f){
for(int i=20;i>=0;i--){
int p=val>>i&1;
if(!trie[it][p])trie[it][p]=++tot;
it=trie[it][p];
sztree[it]+=f;
}
}
int query(int it,int x,int y){
int tot=0;
for(int i=20;i>=0;i--){
if(y>>i&1){
it=trie[it][(x>>i&1)^1];
}
else {
tot+=sztree[trie[it][(x>>i&1)^1]];
it=trie[it][x>>i&1];
}
}
return tot;
}
//fixed
void dfs2(int u){
dfn[u]=++num;
if(son[u])dfs2(son[u]);
rt[u]=++tot;
for(auto v:G[u]){
if(!dfn[v]){
dfs2(v);
ins(rt[u],0,1);
}
}
}
void dfs3(int u,int topf){
for(auto v:G[u]){
if(v!=topf){
if(dp[v]+1==dp[u]){
dpfa[v]=dp2[u]+1;
}
else{
dpfa[v]=dp[u]+1;
}
if(dpfa[v]>dp[v])dp2[v]=dp[v],dp[v]=dpfa[v];
else if(dpfa[v]>dp2[v])dp2[v]=dpfa[v];
dfs3(v,u);
}
}
}
//fixed
void add(int x,int y){
assert(x);
for(;x<=n;x+=x&-x)bitxor[x]^=y;
}
void addseq(int l,int r,int x){
assert(l<=r);
add(l,x),add(r+1,x);
}
//fixed
int getval(int x){
int tot=0;
for(x=dfn[x];x;x-=x&-x)tot^=bitxor[x];
return tot;
}
int solve(int x){
return x&&getval(x)>dp[x];
}
void update(int x){
//fixed
if(son[x]&&dp[x]!=dpfa[x]){
addseq(1,n,dp[x]);
addseq(dfn[son[x]],dfn[son[x]]+sz[son[x]]-1,dp[x]^dp2[x]);
}
else{
addseq(1,n,dp2[x]);
addseq(dfn[x],dfn[x]+sz[x]-1,dp2[x]^dp[x]);
if(fa[x]&&son[fa[x]]!=x){
ins(rt[fa[x]],c[x],-1);
c[x]^=dp[x]^dp2[x];
ins(rt[fa[x]],c[x],1);
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>type>>n>>m;
//fixed
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
G[u].pb(v),G[v].pb(u);
}
dfs(1,0),dfs2(1);
dfs3(1,0);
for(int i=1;i<=n;i++){
int x;cin>>x;
if(x&1)update(i);
}
for(int i=1;i<=m;i++){
int x,y;cin>>x>>y;
update(x);
int res=solve(y)+solve(son[y])+solve(fa[y])+query(rt[y],getval(y),dp[y]+1);
cout<<res<<"\n";
}
}