zoukankan      html  css  js  c++  java
  • [luogu3384] 【模板】树链剖分

    题目大意:

    题目描述

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

    操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z

    操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和

    操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z

    操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和

    解题关键:树链剖分模板+线段树处理区间解决。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    using namespace std;
    const int maxn=1e5+10;
    struct edge{
        int nxt,to;
    }e[maxn*2];
    int n,m,r,mod,v[maxn],head[maxn],cnt,f[maxn],d[maxn],son[maxn],size[maxn],top[maxn],id[maxn],rk[maxn];
    void add_edge(int u,int v){
        e[cnt].to=v;
        e[cnt].nxt=head[u];
        head[u]=cnt++;
    }
    void dfs1(int x){
        size[x]=1,d[x]=d[f[x]]+1;
        for(int v,i=head[x];~i;i=e[i].nxt)
            if((v=e[i].to)!=f[x]){
                f[v]=x,dfs1(v),size[x]+=size[v];
                if(size[son[x]]<size[v])
                    son[x]=v;
            }
    }
    void dfs2(int x,int tp){
        top[x]=tp,id[x]=++cnt,rk[cnt]=x;//?rk不是
        if(son[x])dfs2(son[x],tp);
        for(int v,i=head[x];~i;i=e[i].nxt)
            if((v=e[i].to)!=f[x]&&v!=son[x])
                dfs2(v,v);
    }
    
    //-------------------------------------- 以下为线段树
    #define lson rt<<1,l,mid
    #define rson rt<<1|1,mid+1,r
    int a[maxn<<2],laz[maxn<<2];
    void pushup(int rt){
        a[rt]=(a[rt<<1]+a[rt<<1|1])%mod;
    }
    
    void pushdown(int rt,int lenn){
        if(laz[rt]){
            laz[rt<<1]+=laz[rt];
            laz[rt<<1|1]+=laz[rt];
            a[rt<<1]+=laz[rt]*(lenn-(lenn>>1));a[rt<<1]%=mod;
            a[rt<<1|1]+=laz[rt]*(lenn>>1);a[rt<<1|1]%=mod;
            laz[rt]=0;
        }
    }
    
    void build(int rt,int l,int r){
        if(l==r){
            a[rt]=v[rk[l]]%mod;
            return;
        }
        int mid=l+r>>1;
        build(lson);
        build(rson);
        pushup(rt);
    }
    
    int query(int rt,int l,int r,int L,int R){
        if(L<=l&&r<=R) return a[rt]%mod;
        pushdown(rt,r-l+1);
        int mid=l+r>>1,res=0;
        if(L<=mid)res+=query(lson,L,R);
        if(R>mid) res+=query(rson,L,R);
        return res%mod;
    }
    
    void update(int rt,int l,int r,int L,int R,int k){
        if(L<=l&&r<=R){
            laz[rt]+=k;
            laz[rt]%=mod;
            a[rt]+=k*(r-l+1);
            a[rt]%=mod;
            return;
        }
        int mid=l+r>>1;
        pushdown(rt,r-l+1);
        if(L<=mid)update(lson,L,R,k);
        if(R>mid)update(rson,L,R,k);
        pushup(rt);
    }
    
    int qRange(int x,int y){
        int ans=0;
        while(top[x]!=top[y]){//当两个点不在同一条链上
            if(d[top[x]]<d[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点
            ans+=query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和
            ans%=mod;//按题意取模
            x=f[top[x]];//把x跳到x所在链顶端的那个点的上面一个点
        }
        if(d[x]>d[y])swap(x,y);//把x点深度更深的那个点
        ans+=query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可
        return ans%mod;
    }
    
    inline void updRange(int x,int y,int k){//同上
        k%=mod;
        while(top[x]!=top[y]){
            if(d[top[x]]<d[top[y]])swap(x,y);
            update(1,1,n,id[top[x]],id[x],k);
            x=f[top[x]];
        }
        if(d[x]>d[y])swap(x,y);
        update(1,1,n,id[x],id[y],k);
    }
    
    int qSon(int x){return query(1,1,n,id[x],id[x]+size[x]-1);}
    void updSon(int x,int k){update(1,1,n,id[x],id[x]+size[x]-1,k);}
    
    
    int main(){
        memset(head,-1,sizeof head);
        scanf("%d%d%d%d",&n,&m,&r,&mod);
        for(int i=1;i<=n;i++)scanf("%d",&v[i]);
        for(int x,y,i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            add_edge(x,y),add_edge(y,x);
        }
        cnt=0,dfs1(r),dfs2(r,r);
        build(1,1,n);
        for(int op,x,y,k,i=1;i<=m;i++){
            scanf("%d",&op);
            if(op==1){
                scanf("%d%d%d",&x,&y,&k);
                updRange(x,y,k);
            }
            else if(op==2){
                scanf("%d%d",&x,&y);
                printf("%d
    ",qRange(x,y));
            }
            else if(op==3){
                scanf("%d%d",&x,&y);
                updSon(x,y);
            }
            else{
                scanf("%d",&x);
                printf("%d
    ",qSon(x));
            }
        }
        return 0;
    }
  • 相关阅读:
    [转]Linux(Ubuntu)下如何安装JDK
    第一个MICO CORBA demo实录
    解决/usr/bin/ld: cannot find -lssl
    使用adb shell 进入手机修改文件的权限
    解决某些Android Permission denied
    Java 8新特性终极指南
    Win10系统出问题?简单一招即可修复win10!
    运行时数据区
    linux下vi命令大全
    关于java中final关键字与线程安全性
  • 原文地址:https://www.cnblogs.com/elpsycongroo/p/10389991.html
Copyright © 2011-2022 走看看