一棵 n 个点的树,有点权,q 次操作,每次为以下两种中的一种:
n,q≤105,∑k≤106
很明显可以用虚树做,每次查询建虚树,点权就是当前点到虚树上的父亲的原树上点权和(不包含父亲)。
那么应该需要维护 sumi 代表 i 号点到根的距离以方便查询。单点修改对 sum 数组的影响可以看作子树修改。于是用 dfs序+树状数组+差分 维护 sum 数组。
然而除了样例都过不去,自闭了
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=1e5+5,M=1e6+5;
typedef long long ll;
char buf[1<<20];
int ppp,qqq;
inline char gc()
{
if(ppp==qqq) ppp=0,qqq=fread(buf,1,1<<20,stdin);
return buf[ppp++];
}
#define gc() getchar()
inline int rd()
{
char x;
while((x=gc())<'0'||x>'9');int t=x-'0';
while((x=gc())>='0'&&x<='9') t=t*10+x-'0';
return t;
}
// 以上是读入
int n,bg[N],nx[N*2],to[N*2],val[N],tl,in[N],out[N],link[N],fa[N][20],dep[N],sum[N],dfn;
ll tr[N];
inline void modify(int pos,int v){for(int i=pos;i<=n;i+=i&-i) tr[i]+=v;}
inline ll query(int pos){ll ret=0;for(int i=pos;i;i-=i&-i) ret+=tr[i];return ret;}
inline void add(int x,int y)
{
nx[++tl]=bg[x];
bg[x]=tl;
to[tl]=y;
}
void dfs(int now,int f,int depth)//预处理 dfs 序,倍增数组,sum 数组
{
fa[now][0]=f;dep[now]=depth;
for(int i=1;i<20;i++)
{
if(!fa[fa[now][i-1]][i-1]) break;
fa[now][i]=fa[fa[now][i-1]][i-1];
}
sum[now]=sum[f]+val[now];
link[++dfn]=now;in[now]=dfn;
for(int i=bg[now];i;i=nx[i])
{
int aim=to[i];
if(aim==f) continue;
dfs(aim,now,depth+1);
}
out[now]=dfn;
}
int lca(int x,int y)//倍增 lca
{
if(dep[x]<dep[y]) swap(x,y);
int i,delta=dep[x]-dep[y];
for(i=0;i<20;i++)
{
if((1<<i)>delta) break;
if((1<<i)&delta) x=fa[x][i];
}
if(x==y) return x;
for(i=19;i>0;i--)
{
if(fa[x][i]==fa[y][i]) continue;
x=fa[x][i],y=fa[y][i];
}
return fa[x][0];
}
int qry[M],cnt,stk[M],top;
inline int cmp(int x,int y){return in[x]<in[y];}
int main()
{
n=rd();
int q=rd(),i,j;
for(i=1;i<=n;i++) val[i]=rd();
for(i=1;i<n;i++)
{
int x=rd(),y=rd();
add(x,y),add(y,x);
}
dfs(1,0,1);
for(i=1;i<=n;i++) modify(i,sum[link[i]]-sum[link[i-1]]);//初始化差分树状数组
for(i=1;i<=q;i++)
{
char typ=gc();
if(typ=='Q')
{
ll ans=0;
cnt=0;
int x,tlca;
while((x=rd())) qry[++cnt]=x;
sort(qry+1,qry+cnt+1,cmp);
top=1;stk[1]=1;
for(j=1;j<=cnt;j++)//虚树
{
if(j==1) tlca=qry[j];
else tlca=lca(tlca,qry[j]);
int anc=lca(qry[j],stk[top]);
if(anc!=stk[top])
{
while(dep[anc]<dep[stk[top-1]])
{
//printf("%d %d %lld\n",stk[top-1],stk[top],query(in[stk[top]])-query(in[stk[top-1]]));
ans+=query(in[stk[top]])-query(in[stk[top-1]]);
top--;
}
//printf("%d %d %lld\n",anc,stk[top],query(in[stk[top]])-query(in[anc]));
ans+=query(in[stk[top--]])-query(in[anc]);
if(anc!=stk[top]) stk[++top]=anc;
}
stk[++top]=qry[j];
}
while(top>2)
{
//printf("%d %d %lld\n",stk[top-1],stk[top],query(in[stk[top]])-query(in[stk[top-1]]));
ans+=query(in[stk[top]])-query(in[stk[top-1]]);
top--;
}
//特判根节点未被统计
if(top==2)
{
if(tlca==1) ans+=query(in[stk[2]]);//1为虚树根
else ans+=query(in[stk[2]])-query(in[fa[stk[2]][0]]);//1不为虚树根
}
else if(cnt>=2&&tlca==1) ans+=query(in[1]);//不知道这句话是不是对的
printf("%lld\n",ans);
}
else
{
int pos=rd(),v=rd();
modify(in[pos],v-val[pos]);
modify(out[pos]+1,val[pos]-v);
val[pos]=v;
}
}
return 0;
}
/*
4 4
10 5 2 2
1 2
1 3
1 4
Q 3 4 0
C 3 200
Q 3 4 0
Q 1 2 0
*/