首先,如果不涉及什么换根的骚操作,这显然就是一个
s
g
sg
sg函数的模板题。
在每个点的位置上放棋子的
s
g
sg
sg函数值是它子树内其他点
s
g
sg
sg函数值的
m
e
x
mex
mex,由于这些点的
s
g
sg
sg函数值是连续的,所以点
x
x
x的
s
g
sg
sg函数值就是
x
x
x子树的高度。
不同点的
s
g
sg
sg函数值是异或起来的,所以只要这些点的异或和大于整棵树的高度,先手就获胜,否则后手获胜。
但我们在这道题内,要在修改点的同时,维护所有点的
s
g
sg
sg函数值异或和,并且还要维护所有点的合法性,看着有点难办。
我们先想想怎么维护所有点的
s
g
sg
sg函数异或和。
更改点
x
x
x对点
y
y
y的贡献,相当于就是以
y
y
y为根时,
x
x
x子树内到
x
x
x的最长距离。
显然,这个值最多有两个不同的,也就是最高的子树与次高的子树。
也就是说,在最高的子树内,贡献是次高值,子树外是最高值。
这也就是说,我们按照
d
f
s
dfs
dfs序排序后,就可以直接区间进行修改了。
我们对最大子树内异或上次大值,其余异或上最大值,用线段树就能维护每个点的异或和。
但我们的目的是查询一个点和与它相邻的点有多少能做根。
这也就是说,询问与它相邻的点大于该点最深子树的个数。
可以观察到,其实与它相邻的大部分点的最深子树的深度都是相同的,肯定都是到直径的端点的,所以说,一个点的非长儿子,它们对应的最深子树其实都是延伸到长儿子内的。
也就是说,对于一个点,我们只需要查询它的轻儿子中有多少个小于长儿子深度加
1
1
1的,剩下的长儿子、父亲、和它自身我们可以单独算。
上面的查询也就意味着我们得把它们扔到一个以权值为序的数据结构里,还得在修改时维护它们权值的变化。
由于我们修改时异或的形式,所以
T
r
i
e
Trie
Trie树是相当好的一个选择。
可以发现,一个点的所有轻儿子在修改时进行的变化除了被修改的那个点都是相同的,所以我们可以考虑直接另开一棵树给
T
r
i
e
Trie
Trie树维护它的全局标记。
查询的时候就把这个标记给拿出来,与标记相同的这边就是
0
0
0,不同的就是
1
1
1。
这样就能比较方便地在
T
r
i
e
Trie
Trie树上计算答案。
至于修改的那个点,就在它的父亲的
T
r
i
e
Trie
Trie上改改就行了。
时间复杂度
O
(
(
n
+
q
)
log
n
)
O\left((n+q)\log n\right)
O((n+q)logn)。
另外就是经
S
i
R
i
e
h
n
_
n
x
\rm S\color{red}{iRiehn\_nx}
SiRiehn_nx提醒,直接动态开点空间会炸,要加上节点回收。
#include
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 1000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,a[MAXN],head[MAXN],sz[MAXN],tot,ord[MAXN],sum[MAXN],root[MAXN];
int dfn[MAXN],rd[MAXN],idx,hp[MAXN],wson[MAXN],gp[MAXN],father[MAXN];pii mx[MAXN];
struct edge{int from,to,nxt,op;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){u,v,head[u]};head[u]=tot;}
class SegmentTree{
private:
int tr[MAXN<<2],ip[MAXN],lim;
public:
void build(int rt,int l,int r){
lim=1;while(lim<r-l+1)lim<<=1;
for(int i=l;i<=r;i++)ip[i]=lim+i-1;
}
void modify(int l,int r,int aw){
if(l>r||r<1||l>lim)return ;
int nowl=ip[l],nowr=ip[r];
while(nowl<nowr){
if(nowl&1)tr[nowl]^=aw,nowl++;
if(nowr+1&1)tr[nowr]^=aw,nowr--;
nowl>>=1;nowr>>=1;
}
if(nowl==nowr)tr[nowl]^=aw;
}
int query(int ai){
int now=ip[ai],res=0;
while(now)res^=tr[now],now>>=1;
return res;
}
}T,Tp;
class TrieTree{
private:
int sum[MAXN*20],ch[MAXN*20][2],sta[MAXN*20],stak;
int newnode(){return stak?sta[stak--]:++tot;}
public:
void insert(int id,int ai,int aw){
if(!root[id])root[id]=newnode();int now=root[id];
sum[now]+=aw;if(!sum[now])sta[++stak]=now,root[id]=0;
for(int i=19;i>=0;i--){
int t=(ai>>i)&1;if(!ch[now][t])ch[now][t]=newnode();
int las=now;now=ch[now][t];sum[now]+=aw;
if(!sum[now])ch[las][t]=0,sta[++stak]=now;
}
}
int query(int id,int k,int kp){
int res=0,now=root[id];k^=kp;
for(int i=19;i>=0&&now;i--){
int t=(k>>i)&1,tp=(kp>>i)&1;
if(t==tp)res+=sum[ch[now][t^1]];
now=ch[now][t];
}
return res;
}
}S;
void dosaka1(int u,int fa){
dfn[u]=++idx;ord[idx]=u;father[u]=fa;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa)continue;
dosaka1(v,u);hp[u]=max(hp[v]+1,hp[u]);
if(hp[v]>=hp[wson[u]])wson[u]=v;
}
rd[u]=idx;
}
void dosaka2(int u,int fa){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,tmp=gp[v]+1;
if(tmp>mx[u].fir)mx[u].sec=mx[u].fir,mx[u].fir=tmp;
else if(tmp>mx[u].sec)mx[u].sec=tmp;
}
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa)continue;
if(mx[u].fir==gp[v]+1)gp[u]=mx[u].sec;
else gp[u]=mx[u].fir;
dosaka2(v,u);
}
}
int main(){
//freopen("classic.in","r",stdin);
//freopen("classic.out","w",stdout);
int pts;read(pts);
read(n);read(m);
for(int i=1;i<n;i++){
int u,v;read(u);read(v);
addEdge(u,v);addEdge(v,u);
}
for(int i=1;i<=n;i++)read(a[i]);
dosaka1(1,0);for(int i=1;i<=n;i++)gp[i]=hp[i];
dosaka2(1,0);T.build(1,1,n);Tp.build(1,1,n);
for(int i=1;i<=n;i++)if(a[i]&1){
if(wson[i]&&hp[wson[i]]+1==mx[i].fir)
T.modify(1,n,mx[i].fir),
T.modify(dfn[wson[i]],rd[wson[i]],mx[i].sec^mx[i].fir);
else T.modify(1,n,mx[i].sec),
T.modify(dfn[i],rd[i],mx[i].fir^mx[i].sec);
}
for(int i=1;i<=n;i++)sum[i]=T.query(dfn[i]);
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=e[j].nxt)
if(e[j].to!=wson[i]&&e[j].to!=father[i])
S.insert(i,sum[e[j].to],1);
for(int i=1;i<=m;i++){
int x,y;read(y);read(x);
if(wson[y]&&hp[wson[y]]+1==mx[y].fir){
if(father[y]&&wson[father[y]]!=y)S.insert(father[y],T.query(dfn[y])^Tp.query(dfn[father[y]]),-1);
T.modify(1,n,mx[y].fir);T.modify(dfn[wson[y]],rd[wson[y]],mx[y].sec^mx[y].fir);
Tp.modify(1,n,mx[y].fir);Tp.modify(dfn[wson[y]],rd[wson[y]],mx[y].sec^mx[y].fir);
if(father[y]&&wson[father[y]]!=y)S.insert(father[y],T.query(dfn[y])^Tp.query(dfn[father[y]]),1);
}
else{
if(father[y]&&wson[father[y]]!=y)S.insert(father[y],T.query(dfn[y])^Tp.query(dfn[father[y]]),-1);
T.modify(1,n,mx[y].sec);T.modify(dfn[y],rd[y],mx[y].sec^mx[y].fir);
Tp.modify(1,n,mx[y].sec);Tp.modify(dfn[y],rd[y],mx[y].sec^mx[y].fir);
if(father[y]&&wson[father[y]]!=y)S.insert(father[y],T.query(dfn[y])^Tp.query(dfn[father[y]]),1);
}
int ans=S.query(x,mx[x].fir+1,Tp.query(dfn[x]));
if(T.query(dfn[x])>mx[x].fir)ans++;
if(wson[x]&&T.query(dfn[wson[x]])>mx[wson[x]].fir)ans++;
if(father[x]&&T.query(dfn[father[x]])>mx[father[x]].fir)ans++;
printf("%d\n",ans);
}
return 0;
}