题目描述:
给定一棵以1为根的有根树,点可能是黑色或白色,操作如下。
- 选定一个点x,将x的子树中所有到x的距离为奇数的点的颜色反转。
- 选定一个点x,将点x的颜色反转。
- 选定一个点x,询问所有黑点y(包括点x)与点x的lca(最近公共祖先)的和。 (标号)、
n , m ≤ 200000 n,m\le200000 n,m≤200000
题目分析:
这狗x数据结构写得我头皮发麻
首先这个与所有黑点的LCA的标号和就很标新立异
将每个点的权值记为
(
u
−
f
a
[
u
]
)
∗
子
树
中
的
黑
点
个
数
(u-fa[u])*子树中的黑点个数
(u−fa[u])∗子树中的黑点个数,求
x
x
x的答案就查询
x
x
x到根的链的权值和。
可以发现这样算每个黑点和
x
x
x的LCA到根的权值和恰好就是LCA的标号。
对于单点颜色翻转和单点求答案都可以用树链剖分easy维护。但是这个子树翻转。。。还分距离奇偶。。。
于是我们将深度为奇数的黑点的贡献和深度为偶数的黑点的贡献分开统计,即每个点有两个权值 v 0 , v 1 v_0,v_1 v0,v1,分别表示 ( u − f a [ u ] ) ∗ ( 子 树 中 深 度 为 偶 数 / 奇 数 的 黑 点 个 数 ) (u-fa[u])*(子树中深度为偶数/奇数的黑点个数) (u−fa[u])∗(子树中深度为偶数/奇数的黑点个数)
对于这两个权值我们开两棵线段树来维护。
子树翻转我们需要将子树中每个点的权值由黑点贡献改为白点贡献,所以两种点的贡献我们要同时维护。
子树翻转时对于往上的链我们需要黑点/白点个数的变化,所以需要维护区间黑点/白点的个数,并且翻转时交换这两个值。
注意线段树build时每个点的权值是有初值的,dfs时统计。
下面是写代码的草稿:
维护 (u-fa[u]) * 子树黑点个数
单点修改:查询颜色,链加 (点数变化),修改颜色
单点查询:链和 (sum)
子树翻转:求出子树信息并往上修改链的权值,对于子树交换维护的白点与黑点的信息
a
d
d
[
x
]
add[x]
add[x] : 子树黑点个数变化 (影响区间sum,不改变sp)
v
a
l
[
x
]
val[x]
val[x] : 区间
∑
u
−
f
a
[
u
]
\sum u-fa[u]
∑u−fa[u]
s
p
[
x
]
[
0
/
1
]
sp[x][0/1]
sp[x][0/1] : 区间颜色为
0
/
1
0/1
0/1的点个数,可用于单点查询颜色 子树翻转时交换
s
u
m
[
x
]
[
0
/
1
]
sum[x][0/1]
sum[x][0/1] : 区间
∑
(
u
−
f
a
[
u
]
)
∗
(
u
子
树
颜
色
为
0
/
1
的
点
个
数
)
\sum (u-fa[u])*(u子树颜色为0/1的点个数)
∑(u−fa[u])∗(u子树颜色为0/1的点个数),子树翻转时交换
其实理解怎么做子树翻转之后还是蛮好做的,码着还是蛮爽的。(虽然光看题解就看了我2小时)
Code:
#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,a[maxn],siz[maxn],fa[maxn],top[maxn],son[maxn],dep[maxn],in[maxn],out[maxn],tim,ln[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
int f[maxn][2][2];
void dfs1(int u,int ff){
siz[u]=1,dep[u]=dep[fa[u]=ff]+1;
f[u][dep[u]&1][a[u]]=1;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
dfs1(v,u),siz[u]+=siz[v],siz[v]>siz[son[u]]&&(son[u]=v);
for(int j=0;j<2;j++) for(int k=0;k<2;k++) f[u][j][k]+=f[v][j][k];
}
}
void dfs2(int u,int tp){
top[u]=tp,ln[in[u]=++tim]=u;
if(son[u]) dfs2(son[u],tp);
for(int i=fir[u],v;i;i=nxt[i]) if(!top[v=to[i]]) dfs2(v,v);
out[u]=tim;
}
struct Seg{
#define lc i<<1
#define rc i<<1|1
int sp[maxn<<2][2],add[maxn<<2];
bool rev[maxn<<2];
LL sum[maxn<<2][2],val[maxn<<2];
void updsum(int i){
sum[i][0]=sum[lc][0]+sum[rc][0];
sum[i][1]=sum[lc][1]+sum[rc][1];
}
void updsp(int i){
sp[i][0]=sp[lc][0]+sp[rc][0];
sp[i][1]=sp[lc][1]+sp[rc][1];
}
void tag_rev(int i){
add[i]=-add[i],rev[i]^=1;
swap(sp[i][0],sp[i][1]),swap(sum[i][0],sum[i][1]);
}
void tag_add(int i,int v){
add[i]+=v;
sum[i][1]+=1ll*v*val[i], sum[i][0]-=1ll*v*val[i];
}
void down(int i){
if(rev[i]) tag_rev(lc),tag_rev(rc),rev[i]=0;
if(add[i]) tag_add(lc,add[i]),tag_add(rc,add[i]),add[i]=0;
}
void build(int i,int l,int r,bool flg){
if(l==r){
int x=ln[l]; val[i]=x-fa[x];
if(dep[x]%2==flg) sp[i][a[x]]=1;
sum[i][0]=1ll*f[x][flg][0]*val[i];
sum[i][1]=1ll*f[x][flg][1]*val[i];
return;
}
int mid=(l+r)>>1;
build(lc,l,mid,flg),build(rc,mid+1,r,flg);
val[i]=val[lc]+val[rc];
updsum(i),updsp(i);
}
void mdf(int i,int l,int r,int x,int y,int v){//mdf sum (subtree add v black points.)
if(x<=l&&r<=y) return tag_add(i,v);
int mid=(l+r)>>1; down(i);
if(x<=mid) mdf(lc,l,mid,x,y,v); if(y>mid) mdf(rc,mid+1,r,x,y,v);
updsum(i);
}
void Revp(int i,int l,int r,int x){//rev x's color.
if(l==r) return swap(sp[i][0],sp[i][1]);
int mid=(l+r)>>1; down(i);
x<=mid?Revp(lc,l,mid,x):Revp(rc,mid+1,r,x);
updsp(i);
}
void Rev(int i,int l,int r,int x,int y){//rev the subtree (sum swap, color swap)
if(x<=l&&r<=y) return tag_rev(i);
int mid=(l+r)>>1; down(i);
if(x<=mid) Rev(lc,l,mid,x,y); if(y>mid) Rev(rc,mid+1,r,x,y);
updsum(i),updsp(i);
}
int qsp(int i,int l,int r,int x,int y){//[x,y] col=1 numbers.
if(x<=l&&r<=y) return sp[i][1];
int mid=(l+r)>>1; down(i);
return (x<=mid?qsp(lc,l,mid,x,y):0)+(y>mid?qsp(rc,mid+1,r,x,y):0);
}
LL qsum(int i,int l,int r,int x,int y){
if(x<=l&&r<=y) return sum[i][1];
int mid=(l+r)>>1; down(i);
return (x<=mid?qsum(lc,l,mid,x,y):0)+(y>mid?qsum(rc,mid+1,r,x,y):0);
}
}T[2];
int main()
{
//freopen("1.in","r",stdin);
int op,x,y;
read(n),read(m);
for(int i=1;i<=n;i++) read(a[i]);
for(int i=1;i<n;i++) read(x),read(y),line(x,y),line(y,x);
dfs1(1,0),dfs2(1,1);
T[0].build(1,1,n,0),T[1].build(1,1,n,1);
while(m--){
read(op),read(x);
if(op==1){
int c=!(dep[x]&1), p1=T[c].qsp(1,1,n,in[x],out[x]), ps=f[x][c][0]+f[x][c][1];
T[c].Rev(1,1,n,in[x],out[x]);
for(x=fa[x];x;x=fa[top[x]]) T[c].mdf(1,1,n,in[top[x]],in[x],ps-2*p1);
}
if(op==2){
int c=dep[x]&1,col=T[c].qsp(1,1,n,in[x],in[x]);
T[c].Revp(1,1,n,in[x]);
for(;x;x=fa[top[x]]) T[c].mdf(1,1,n,in[top[x]],in[x],col?-1:1);
}
if(op==3){
LL ans=0;
for(;x;x=fa[top[x]]) ans+=T[0].qsum(1,1,n,in[top[x]],in[x])+T[1].qsum(1,1,n,in[top[x]],in[x]);
printf("%lld\n",ans);
}
}
}
/*
~~这狗x数据结构做得我头皮发麻~~
维护 (u-fa[u]) * 子树黑点个数
单点修改:查询颜色,链加 (点数变化),修改颜色
单点查询:链和 (sum)
子树翻转:求出子树信息并往上修改链的权值,对于子树交换维护的白点与黑点的信息
对于深度为奇数和偶数的点分开维护,即对于T[0/1]维护(u-fa[u])*(子树中深度为偶数/奇数的黑点个数)
add[x] : 子树黑点个数变化 (影响区间sum,不改变sp)
val[x] : 区间 \sum u-fa[u]
sp[x][0/1] : 区间颜色为0/1的点个数,可用于单点查询颜色 子树翻转时交换
sum[x][0/1] : 区间 \sum (u-fa[u])*(u子树颜色为0/1的点个数) ,子树翻转时交换
*/