有一棵以 1 为根的 n 个点的树,每个点有黑白两种颜色以及一个权值(一开始都是零)。
有 q 个操作,有以下两种:
∙ 选定一个点 x ,将所有黑色节点 y 的权值加上 lca(x,y)
∙ 将 x 号点的颜色反色
在所有操作执行完后,请输出所有点的权值。
1≤n,q≤5×104
将每个点的编号都和父亲编号做差,这样一个点对权值的贡献就可以看成起到根路径的和再乘上这个点作为 1 操作的点的祖先的次数。
考虑使用数据结构维护路径上的一个计数器,计算一条路径的编号差与次数乘积之和。因为每次只是修改黑色节点,我们使用差分的思想,当一个点变成黑色点的时候我们把它的答案减去其到根路径的权值和,变成白色时加上这个权值和。最后再处理没有变回白色的点。
数据结构使用 LCT 即可做到 O(nlogn) 。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <stack>
using namespace std;
typedef long long LL;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
int buf[30];
void write(LL x)
{
if (x<0) putchar('-'),x=-x;
for (;x;x/=10) buf[++buf[0]]=x%10;
if (!buf[0]) buf[++buf[0]]=0;
for (;buf[0];putchar('0'+buf[buf[0]--]));
}
const int N=50050;
const int E=N<<1;
int fa[N],last[N];
int tov[E],nxt[E];
bool col[N];
LL ans[N];
int n,q,tot;
namespace link_cut_tree
{
int fa[N],size[N],par[N],tag[N],val[N];
LL sum[N],f[N],valsum[N];
stack<int> st;
int son[N][2];
bool mark[N];
bool side(int x){return son[fa[x]][1]==x;}
void update(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1,valsum[x]=valsum[son[x][0]]+valsum[son[x][1]]+val[x],sum[x]=sum[son[x][0]]+sum[son[x][1]]+f[x];}
void ADD(int x,int delta){tag[x]+=delta,f[x]+=1ll*delta*val[x],sum[x]+=1ll*delta*valsum[x];}
void R(int x){swap(son[x][0],son[x][1]),mark[x]^=1;}
void clear(int x)
{
if (mark[x])
{
if (son[x][0]) R(son[x][0]);
if (son[x][1]) R(son[x][1]);
mark[x]=0;
}
if (tag[x])
{
if (son[x][0]) ADD(son[x][0],tag[x]);
if (son[x][1]) ADD(son[x][1],tag[x]);
tag[x]=0;
}
}
void rotate(int x)
{
int y=fa[x];bool s=side(x);
if (fa[y]) son[fa[y]][side(y)]=x;
if (son[x][s^1]) fa[son[x][s^1]]=y;
son[y][s]=son[x][s^1],son[x][s^1]=y;
fa[x]=fa[y],fa[y]=x;
if (par[y]) par[x]=par[y],par[y]=0;
update(y),update(x);
}
void pushdown(int x,int y)
{
for (;x!=y;st.push(x),x=fa[x]);
for (;!st.empty();clear(st.top()),st.pop());
}
void splay(int x,int y)
{
for (pushdown(x,y);fa[x]!=y;rotate(x))
if (fa[fa[x]]!=y)
if (side(x)==side(fa[x])) rotate(fa[x]);
else rotate(x);
}
int access(int x)
{
int nxt=0;
for (;x;update(nxt=x),x=par[x])
{
splay(x,0);
if (son[x][1]) par[son[x][1]]=x,fa[son[x][1]]=0;
if (nxt) par[nxt]=0,fa[nxt]=x;
son[x][1]=nxt;
}
return nxt;
}
void makeroot(int x){R(access(x));}
void modify(int x){makeroot(1),access(x),splay(x,0),ADD(x,1);}
LL query(int x){return makeroot(1),access(x),splay(x,0),sum[x];}
};
void insert(int x,int y){tov[++tot]=y,nxt[tot]=last[x],last[x]=tot;}
void dfs(int x)
{
for (int i=last[x],y;i;i=nxt[i])
if ((y=tov[i])!=fa[x]) fa[y]=x,dfs(y);
link_cut_tree::val[x]=x-fa[x],link_cut_tree::update(x),link_cut_tree::par[x]=fa[x];
}
int main()
{
freopen("bwt.in","r",stdin),freopen("bwt.out","w",stdout);
n=read(),q=read();
for (int i=1;i<=n;++i) col[i]=read();
for (int i=1,x,y;i<n;++i) x=read(),y=read(),insert(x,y),insert(y,x);
fa[1]=0,dfs(1);
for (int tp,x;q--;)
{
tp=read()-1,x=read();
if (tp) ans[x]+=(col[x]?1:-1)*link_cut_tree::query(x),col[x]^=1;
else link_cut_tree::modify(x);
}
for (int x=1;x<=n;++x) if (col[x]) ans[x]+=link_cut_tree::query(x);
for (int x=1;x<=n;++x) write(ans[x]),putchar('\n');
fclose(stdin),fclose(stdout);
return 0;
}