题意:
给出一棵树,root=1,树有点权,有一个人叫做M
有3种操作:
1 u v 把u到v路径上的所有点的点权都给M
2 u 若u的点权在M手上,拿走
3 u 把u为根的子树的所有点权都给M
每一个操作过后,输出M拥有的点权
想法:
要维护路径,用树链剖分
要维护子树,用dfs序
但是这样貌似要写很多
然而后来知道
树链剖分是有dfs序的,也就是说,树链剖分后,对于一个点,其子树所有点的新编号刚好在该点新编号后面的一个连续的区间里面,这个区间的范围[chg[u],chg[u]+siz[u]-1],
这样的话就方便了,我们只需要一个树链剖分就可以了。
树链剖分后用线段树维护,
线段树维护3个值:
all:若区间都被M拥有了,all为1,否则为0
val:在该区间内,M拥有的所有点权的和
sum:在该区间内,所有点的点权的和
(sum在build后就是固定的,不需要再更改)
我们只需要update2个:all和val
这样每一次update后,答案就是seg[1].val了
其实刚开始的时候我只维护2个值:all和sum
然后每一次update操作后我就query一次求出val,然而这样肯定超时啊
加了个val后query函数就直接省略了
#pragma comment(linker, "/STACK:102400000,102400000") #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int maxn=1e5+10; const int inf=0x3f3f3f3f; struct Seg { int all,val,sum; }; Seg seg[maxn<<2]; int a[maxn]; int dep[maxn]; int siz[maxn]; int son[maxn]; int fa[maxn]; int top[maxn]; int chg[maxn]; int rev[maxn]; struct Edge { int to,next; }; Edge edge[maxn<<1]; int head[maxn]; int tot; void init() { memset(head,-1,sizeof head); tot=0; } void addedge(int u,int v) { edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void solve(int ); int main() { int test; scanf("%d",&test); while(test--){ int n; scanf("%d",&n); init(); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<n;i++){ int u,v; scanf("%d %d",&u,&v); addedge(u,v); addedge(v,u); } solve(n); } return 0; } void dfs0(int u,int pre) { fa[u]=pre; siz[u]=1; dep[u]=dep[pre]+1; for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(v==pre) continue; dfs0(v,u); siz[u]+=siz[v]; if(son[u]==-1 || siz[v]>siz[son[u]]) son[u]=v; } } void dfs1(int u,int tp) { top[u]=tp; chg[u]=++tot; rev[tot]=u; if(son[u]==-1) return ; dfs1(son[u],tp); for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(v==fa[u] || v==son[u]) continue; dfs1(v,v); } } void pushup(int rt) { seg[rt].val=seg[rt<<1].val+seg[rt<<1|1].val; } void build(int l,int r,int rt) { seg[rt].sum=seg[rt].all=seg[rt].val=0; if(l==r){ seg[rt].sum=a[rev[l]]; return ; } int m=(l+r)>>1; build(lson); build(rson); seg[rt].sum=seg[rt<<1].sum+seg[rt<<1|1].sum; } void pushdown(int rt) { if(seg[rt].all){ seg[rt<<1].all=seg[rt<<1|1].all=1; seg[rt<<1].val=seg[rt<<1].sum; seg[rt<<1|1].val=seg[rt<<1|1].sum; seg[rt].all=0; } } void update_seg(int L,int R,int add,int l,int r,int rt) { if(L<=l&&R>=r){ seg[rt].all=add; if(add) seg[rt].val=seg[rt].sum; else seg[rt].val=0; return ; } int m=(l+r)>>1; pushdown(rt); if(L<=m) update_seg(L,R,add,lson); if(R>m) update_seg(L,R,add,rson); pushup(rt); } void update_path(int u,int v) { while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); update_seg(chg[top[u]],chg[u],1,1,tot,1); u=fa[top[u]]; } if(dep[u]<dep[v]) swap(u,v); update_seg(chg[v],chg[u],1,1,tot,1); } void solve(int n) { memset(son,-1,sizeof son); memset(dep,0,sizeof dep); dfs0(1,1); tot=0; dfs1(1,1); build(1,tot,1); int q; scanf("%d",&q); for(int i=1;i<=q;i++){ int op; scanf("%d",&op); if(op==1){ int u,v; scanf("%d %d",&u,&v); update_path(u,v); } else{ int u; scanf("%d",&u); if(op==2){ update_seg(chg[u],chg[u],0,1,tot,1); } else{ update_seg(chg[u],chg[u]+siz[u]-1,1,1,tot,1); } } printf("%d ",seg[1].val); } return ; }