[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;
}
View Code

 

posted @ 2020-03-31 12:28  _ZPENG  阅读(139)  评论(0编辑  收藏  举报