[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\n",qRange(x,y));
        }
        else if(op==3){
            scanf("%d%d",&x,&y);
            updSon(x,y);
        }
        else{
            scanf("%d",&x);
            printf("%d\n",qSon(x));
        }
    }
    return 0;
}

 

posted @ 2019-02-17 01:01  Elpsywk  阅读(132)  评论(0编辑  收藏  举报