洛谷3384

树链剖分:

fa[ ]父亲节点;son[ ]重儿子节点;siz[ ]以该点为根的子树的大小;

id[ ]该点在两次dfs后形成序列中的位置;dep[ ]该点深度;

wt[ ]两次dfs后形成的序列;top[ ]该点所在链的链头

 

1.dfs1先求出每个点的深度,父亲节点和以该点为根的子树的大小,并找到其重儿子(及siz最大的儿子)

2.dfs2求出最终形成的序列,并求出每个点所在链的链头(对于一个点,优先遍历重儿子,保证重链在序列中的连续)

3.对形成的序列建议可先段树

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

   先将x,y整条链整条链

的往上跳,直到他们跳到同一条链上,过程中求出每次跳动区间的和,再求出同一条链中的他们之间的值(此时能够保证是最短路,否则他们之前必然能跳到同一条链上)

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

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

 一棵子树的点在序列中必然连续,又已知其siz,则该棵树上的点的区间为【id[x],id[x]+siz[x]-1】

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

同操作3

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#define Rint register int
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std; 
const int maxn=200005;
int n,m,r,mo,ne,res=0,cnt,laz[maxn<<2],w[maxn],fa[maxn],siz[maxn],dep[maxn],tr[maxn<<2],top[maxn],wt[maxn],id[maxn],to[maxn],nex[maxn],head[maxn],son[maxn];

inline void add(int x,int y){
    to[++ne]=y;nex[ne]=head[x];head[x]=ne;
}

inline void read(int &x){
    char ch=getchar();x=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
}

inline void pushdown(int rt,int len){
    if(laz[rt]==0)return;
    laz[rt<<1]+=laz[rt];laz[rt<<1|1]+=laz[rt];
    tr[rt<<1]+=laz[rt]*(len-(len>>1));tr[rt<<1|1]+=laz[rt]*(len>>1);
    tr[rt<<1]%=mo;tr[rt<<1|1]%=mo;
    laz[rt]=0;
}

inline void build(int rt,int l,int r){
    if(l==r){tr[rt]=wt[l]%mo;return;}
    build(lson);build(rson);
    tr[rt]=(tr[rt<<1]+tr[rt<<1|1])%mo;
}

inline void query(int rt,int l,int r,int L,int R){
    if(l>=L&&r<=R){res+=tr[rt];res%=mo;return;}
    pushdown(rt,r-l+1);
    if(mid>=L)query(lson,L,R);
    if(mid<R)query(rson,L,R);
}

inline void updata(int rt,int l,int r,int L,int R,int k){
    if(l>=L&&r<=R){laz[rt]+=k;tr[rt]+=k*(r-l+1);return;}
    pushdown(rt,r-l+1);
    if(mid>=L)updata(lson,L,R,k);
    if(mid<R)updata(rson,L,R,k);
    tr[rt]=(tr[rt<<1]+tr[rt<<1|1])%mo;
}

inline int qRange(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        res=0;
        query(1,1,n,id[top[x]],id[x]);
        ans+=res;ans%=mo;x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    res=0;
    query(1,1,n,id[x],id[y]);
    ans+=res;
    return ans%mo;
}

inline void updRange(int x,int y,int k){//同上 
    k%=mo;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        updata(1,1,n,id[top[x]],id[x],k);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    updata(1,1,n,id[x],id[y],k);
}

inline void updson(int x,int k){
    updata(1,1,n,id[x],id[x]+siz[x]-1,k);
}

inline int qson(int x){
    res=0;
    query(1,1,n,id[x],id[x]+siz[x]-1);return res;
}

inline void dfs1(int x,int f,int depth){
    dep[x]=depth;fa[x]=f;siz[x]=1;
    int maxson=-1;
    for(Rint i=head[x];i;i=nex[i]){
        int y=to[i];
        if(y==f)continue;
        dfs1(y,x,depth+1);
        siz[x]+=siz[y];
        if(siz[y]>maxson){son[x]=y;maxson=siz[y];}
    }
}

inline void dfs2(int x,int topf){
     id[x]=++cnt;
     wt[cnt]=w[x];
     top[x]=topf;
     if(!son[x])return;
     dfs2(son[x],topf);
     for(Rint i=head[x];i;i=nex[i]){
         int y=to[i];
         if(y==fa[x]||y==son[x])continue;
         dfs2(y,y);
     }
} 

int main(){
    read(n);read(m);read(r);read(mo);
    for(Rint i=1;i<=n;i++)read(w[i]);
    for(Rint i=1;i<n;i++){
        int a,b;read(a);read(b);
        add(a,b);add(b,a);
    }
    dfs1(r,0,1);dfs2(r,r);
    build(1,1,n);
    while(m--){
        int typ,x,y,z;
        read(typ);read(x);
        switch(typ){
            case 1:read(y);read(z);updRange(x,y,z);break;
            case 2:read(y);
            printf("%d\n",qRange(x,y));break;
            case 3:read(y);updson(x,y);break;
            case 4:printf("%d\n",qson(x));break;
        }
    }
}

树链剖分代码虽长,但思路非常清晰,思想也容易理解。

posted @ 2018-04-20 22:40  lnyzo  阅读(272)  评论(0编辑  收藏  举报