[P1501][国家集训队]Tree II——LCT维护链乘加
[国家集训队]Tree II
维护链权值和,同时支持链加乘操作。类似线段树维护加乘操作。打两个标记,分别为乘,加,遇到加法直接更新标记,遇到乘法更新两个标记。下放标记时先放乘,后放加。由于pushup要更新sum,所以还需要维护树的大小。
每个Splay的根节点信息不一定要随时正确的,因为所有操作必定调用splay,splay就会更新根节点。所以能够能过pushup更新的信息在根节点不一定正确。要用于更新父节点的非根节点的信息才要即时更新。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll p=51061; const int N=1e5+10; int c[N][2],f[N],st[N];//st为栈 ll v[N],sz[N],sum[N],add[N],mul[N]; bool r[N]; inline bool nroot(int x){//判断节点是否为所在Splay的根 return c[f[x]][0]==x||c[f[x]][1]==x; }//原理:如果是根则是父亲的儿子没有x节点 inline void pushup(int x){ sz[x]=sz[c[x][0]]+sz[c[x][1]]+1; sum[x]=((sum[c[x][0]]+sum[c[x][1]])*mul[x]%p+(sz[x]-1)*add[x]%p+v[x])%p; } inline void pushadd(int x,int c){ add[x]=(add[x]+c)%p; v[x]=(v[x]+c)%p; sum[x]=(sum[x]+sz[x]*c)%p; } inline void pushmul(int x,int c){ add[x]=(add[x]*c)%p; mul[x]=(mul[x]*c)%p; v[x]=(v[x]*c)%p; sum[x]=(sum[x]*c)%p; } inline void pushr(int x){ r[x]^=1; swap(c[x][0],c[x][1]); } inline void pushdown(int x){ if(r[x]){ if(c[x][0])pushr(c[x][0]); if(c[x][1])pushr(c[x][1]); r[x]=0; } if(mul[x]!=1){ if(c[x][0])pushmul(c[x][0],mul[x]); if(c[x][1])pushmul(c[x][1],mul[x]); mul[x]=1; } if(add[x]){ if(c[x][0])pushadd(c[x][0],add[x]); if(c[x][1])pushadd(c[x][1],add[x]); add[x]=0; } } void rotate(int x){ int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k]; if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w; if(w)f[w]=y;f[y]=x;f[x]=z; pushup(y);//pushup(x)可省略 } void splay(int x){ int y=x,z=0; st[++z]=y; while(nroot(y))st[++z]=y=f[y]; while(z)pushdown(st[z--]);//释放根到x的标记 while(nroot(x)){ y=f[x];z=f[y]; if(nroot(y))rotate((c[y][0]==x)^(c[z][0]==y)?x:y); rotate(x); } pushup(x); } void access(int x){//拉出树根到x的链到一个Splay for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y,pushup(x);//删链所以pushup } void makeroot(int x){//换x为原树根 access(x);splay(x); pushr(x); } int findroot(int x){//找x在原树中的根 access(x);splay(x); while(c[x][0])pushdown(x),x=c[x][0]; splay(x);//?? return x; } void split(int x,int y){//拉出x-y链到一个Splay makeroot(x); access(y);splay(y); } void link(int x,int y){ makeroot(x); if(findroot(y)!=x)f[x]=y; } void cut(int x,int y){ makeroot(x); if(findroot(y)==x&&f[y]==x&&!c[y][0]){ f[y]=c[x][1]=0; //pushup(x); } } int main(){ int n,q,x,y,x2,y2,c; scanf("%d%d",&n,&q); for(int i=1;i<=n;++i)mul[i]=v[i]=1;//sz,sum省略 for(int i=1;i<n;++i){ scanf("%d%d",&x,&y); link(x,y); } while(q--){ char op; scanf(" %c",&op); if(op=='+'){ scanf("%d%d%d",&x,&y,&c); split(x,y); pushadd(y,c); }else if(op=='-'){ scanf("%d%d%d%d",&x,&y,&x2,&y2); cut(x,y);link(x2,y2); }else if(op=='*'){ scanf("%d%d%d",&x,&y,&c); split(x,y); pushmul(y,c); }else if(op=='/'){ scanf("%d%d",&x,&y); split(x,y); printf("%lld\n",sum[y]); } } return 0; }