首页 > 其他 > 详细

树链剖分模板(洛谷P3384)

时间:2018-11-22 01:21:08      阅读:159      评论:0      收藏:0      [点我收藏+]

洛谷P3384

#include <bits/stdc++.h>
#define DBG(x) cerr << #x << " = " << x << endl;
const int maxn = 1e5+5;
using namespace std;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch < ‘0‘ || ch > ‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch >= ‘0‘ && ch <= ‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

int n,m,rot,mod;
int w[maxn],wt[maxn];///剖分前后的点权,不要抄错QAQ
int head[maxn],tot;
int cnt,dep[maxn],siz[maxn],fa[maxn],son[maxn],id[maxn],top[maxn];///son[i]表示i的重儿子,top[i]表示i所在链的顶点
int ans;

struct treenode{
    int l,r,val,add;
}tree[maxn<<2];

struct edgenode{
    int to,next;
}edge[maxn<<1];

void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}

void push_up(int x){
    tree[x].val=tree[x<<1].val+tree[x<<1|1].val;
    tree[x].val%=mod;
}

void push_down(int x,int len){
    if(tree[x].add){
        tree[x<<1].add+=tree[x].add;
        tree[x<<1|1].add+=tree[x].add;
        tree[x<<1].val+=(len-(len>>1))*tree[x].add;
        tree[x<<1|1].val+=(len>>1)*tree[x].add;
        tree[x<<1].val%=mod;
        tree[x<<1|1].val%=mod;
        tree[x].add=0;
    }
}

void build(int i,int l,int r){
    tree[i].l=l;
    tree[i].r=r;
    tree[i].add=0;
    if(l == r){
        tree[i].val=wt[l];
        tree[i].val%=mod;
        return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
    push_up(i);
}

void update(int i,int l,int r,int L,int R,int c){
    if(l >= L && r <= R){
        tree[i].val+=(r-l+1)*c;
        tree[i].add+=c;
        return;
    }
    push_down(i,r-l+1);
    int mid=(l+r)>>1;
    if(L <= mid)update(i<<1,l,mid,L,R,c);
    if(R > mid)update(i<<1|1,mid+1,r,L,R,c);
    push_up(i);
}

void query(int i,int l,int r,int L,int R){
    if(l >= L && r <= R){
        ans+=tree[i].val;
        ans%=mod;
        return;
    }
    push_down(i,r-l+1);
    int mid=(l+r)>>1;
    if(L <= mid)query(i<<1,l,mid,L,R);
    if(R > mid)query(i<<1|1,mid+1,r,L,R);
}

void dfs1(int x,int pre,int deep){///确定各节点层次,子树大小,父节点编号,重儿子编号
    dep[x]=deep;
    fa[x]=pre;
    siz[x]=1;
    int maxson=-1;
    for(int i=head[x];i != -1;i=edge[i].next){
        int v=edge[i].to;
        if(v != pre){
            dfs1(v,x,deep+1);
            siz[x]+=siz[v];
            if(siz[v] > maxson){maxson=siz[v];son[x]=v;}
        }
    }
}

void dfs2(int x,int topf){///确定各链的信息,包括链的顶点以及链上点的新编号
    id[x]=++cnt;
    wt[id[x]]=w[x];
    top[x]=topf;
    if(!son[x])return;
    dfs2(son[x],topf);
    for(int i=head[x];i != -1;i=edge[i].next){
        int v=edge[i].to;
        if(v == fa[x] || v == son[x])continue;
        dfs2(v,v);
    }
}

void spilt(){
    dfs1(rot,0,1);
    dfs2(rot,rot);
    build(1,1,n);
}
/*查询两点路径上的点权和*/
int qRange(int x,int y){///让深度深的点往上跳到顶点并更新对答案的贡献
    int res=0;          ///之后更新到旧链顶上的链,重复过程直到两点在同一条链上
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]])swap(x,y);
        ans=0;
        query(1,1,n,id[top[x]],id[x]);
        res+=ans;
        res%=mod;
        x=fa[top[x]];
    }
    if(dep[x] > dep[y])swap(x,y);
    ans=0;
    query(1,1,n,id[x],id[y]);
    res+=ans;
    return res%mod;
}
/*更新同查询*/
void updRange(int x,int y,int c){
    c%=mod;
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]])swap(x,y);
        update(1,1,n,id[top[x]],id[x],c);
        x=fa[top[x]];
    }
    if(dep[x] > dep[y])swap(x,y);
    update(1,1,n,id[x],id[y],c);
}
/*查询子树点权和*/
int qSon(int x){///由于新编号连续,直接查询树根到树根+树的大小,这段区间对应整个子树
    ans=0;
    query(1,1,n,id[x],id[x]+siz[x]-1);
    return ans;
}
/*更新同查询*/
void updSon(int x,int c){
    update(1,1,n,id[x],id[x]+siz[x]-1,c);
}

int main(){
    memset(head,-1,sizeof head);
    n=read(),m=read(),rot=read(),mod=read();
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<=n-1;i++){
        int a=read(),b=read();
        addedge(a,b);
        addedge(b,a);
    }
    spilt();///剖分
    for(int i=1;i<=m;i++){
        int op,x,y,z;
        op=read();
        if(op == 1){
            x=read(),y=read(),z=read();
            updRange(x,y,z);
        }
        if(op == 2){
            x=read(),y=read();
            printf("%d\n",qRange(x,y));
        }
        if(op == 3){
            x=read(),y=read();
            updSon(x,y);
        }
        if(op == 4){
            x=read();
            printf("%d\n",qSon(x));
        }
    }
    return 0;
}

  

树链剖分模板(洛谷P3384)

原文:https://www.cnblogs.com/DuskOB/p/9998579.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!