• [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
    ",sum[y]);
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    这个 bug 让我更加理解 Spring 单例了
    SpringBoot
    codeblocks笔记
    https://docs.platformio.org/en/latest/boards/index.html
    外部存储的烧写
    嵌入式AI
    python的一些库
    语音芯片及解决方案
    神奇的调试值“DEADBEEF”
    【12月】+我与rt_thread的“江湖恩怨”
  • 原文地址:https://www.cnblogs.com/zpengst/p/12603977.html
Copyright © 2020-2023  润新知