洛谷 P3384 【模板】树链剖分

洛谷   P3384 【模板】树链剖分

//树链剖分,用一个线段树处理打过标记的树。
//树链剖分入门详见https://www.luogu.org/blog/communist/shu-lian-pou-fen-yang-xie
//树链剖分模板题可做https://www.luogu.org/problem/P3384   P3384 【模板】树链剖分
//编码过程中遇到   段错误   ,深知是数据越界,跟踪查起。
//dfs1写错,如下
/*
void dfs1(int x){//处理父亲,儿子,自己。
    int b,v;
    d[x]=d[fa[x]]+1,size[x]=1;
    for(b=head[x];b;b=e[b].next){
        v=e[b].to;
        if(v!=fa[x])fa[v]=x,dfs1(v),size[x]+=size[v];
        if(size[son[x]]<size[v])son[x]=v;//重儿子
    }
}
*/
//又遇到段错误
/*
此处错写成if(l==r)a[k].l=l,a[k].r=r,a[k].sum=v[rk[l]],a[k].lazy=0;
少写了return;
*/
//样例通过,提交AC.2019-11-3 17:45
#include <stdio.h>
#include <string.h>
#define maxn 100100
#define lson k<<1
#define rson k<<1|1
int N,M,R,P,v[maxn],head[maxn],cnt,d[maxn],fa[maxn],size[maxn],son[maxn];//d[]深度
int top[maxn],id[maxn],rk[maxn];//id[x]=y   节点x对应访问次序y,rk[y]=x   访问次序y对应节点x
struct node1{
    int to,next;
}e[maxn<<1];//无向图
struct node2{
    int l,r,sum,lazy;
}a[maxn<<2];
void add_edge(int u,int v){//邻接表
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
void dfs1(int x){//处理父亲,儿子,自己。
    int b,v;
    d[x]=d[fa[x]]+1,size[x]=1;
    for(b=head[x];b;b=e[b].next){
        v=e[b].to;
        if(v!=fa[x]){
            fa[v]=x,dfs1(v),size[x]+=size[v];
            if(size[son[x]]<size[v])son[x]=v;//重儿子
        }
    }
}
void dfs2(int x,int tp){//tp链首节点
    int b,v;
    top[x]=tp,cnt++,rk[cnt]=x,id[x]=cnt;//重新标记节点x
    if(son[x])dfs2(son[x],tp);//重儿子,形成重边
    for(b=head[x];b;b=e[b].next){
        v=e[b].to;
        if(v!=fa[x]&&v!=son[x])dfs2(v,v);//轻儿子,形成轻边
    }
}
int len(int k){
    return a[k].r-a[k].l+1;
}
void pushup(int k){
    a[k].sum=a[lson].sum+a[rson].sum;
}
void pushdown(int k){
    if(a[k].lazy){
        a[lson].lazy=(a[lson].lazy+a[k].lazy)%P,a[rson].lazy=(a[rson].lazy+a[k].lazy)%P;
        a[lson].sum=(a[lson].sum+len(lson)*a[k].lazy)%P,a[rson].sum=(a[rson].sum+len(rson)*a[k].lazy)%P;
    }
    a[k].lazy=0;
}
void build(int l,int r,int k){//l,r标记的序列
    int mid=(l+r)/2;
    if(l==r){//此处错写成if(l==r)a[k].l=l,a[k].r=r,a[k].sum=v[rk[l]],a[k].lazy=0;
        a[k].l=l,a[k].r=r,a[k].sum=v[rk[l]],a[k].lazy=0;
        return;
    }
    build(l,mid,lson),build(mid+1,r,rson);
    a[k].l=l,a[k].r=r,pushup(k);
}
void update(int l,int r,int c,int k){//l,r标记的序列
    int mid=(a[k].l+a[k].r)/2;
    if(l<=a[k].l&&a[k].r<=r){
        a[k].sum=(a[k].sum+len(k)*c)%P,a[k].lazy=(a[k].lazy+c)%P;
        return;
    }
    pushdown(k);//顺便更新线段树下面的点。
    if(l<=mid)update(l,r,c,lson);
    if(mid+1<=r)update(l,r,c,rson);
    pushup(k);
}
int query(int l,int r,int k){//l,r标记的序列
    int ret=0,mid=(a[k].l+a[k].r)/2;
    if(l<=a[k].l&&a[k].r<=r){
        ret=(ret+a[k].sum)%P;
        return ret;
    }
    pushdown(k);//要用到lson,rson的数据,不得不更新
    if(l<=mid)ret=(ret+query(l,r,lson))%P;
    if(mid+1<=r)ret=(ret+query(l,r,rson))%P;
    return ret;
}
void swap(int *x,int *y){
    int t;
    t=*x,*x=*y,*y=t;
}
void updates(int x,int y,int c,int k){//x,y 是原树中具体节点
    while(top[x]!=top[y]){//不在同一链上
        if(d[top[x]]<d[top[y]])swap(&x,&y);//目的,使链首top[x]深度更深
        update(id[top[x]],id[x],c,1),x=fa[top[x]];//更新同一链上数据;x=fa[top[x]]找新的链
    }
    if(id[x]>id[y])swap(&x,&y);//同一链上,希望id[x]序列更小
    update(id[x],id[y],c,1);
}
int sum(int x,int y,int k){//x,y 是原树中具体节点
    int ret=0;
    while(top[x]!=top[y]){//不在同一链上
        if(d[top[x]]<d[top[y]])swap(&x,&y);//目的,使链首top[x]深度更深
        ret=(ret+query(id[top[x]],id[x],1))%P,x=fa[top[x]];//更新同一链上数据;x=fa[top[x]]找新的链
    }
    if(id[x]>id[y])swap(&x,&y);//同一链上,希望id[x]序列更小
    ret=(ret+query(id[x],id[y],1))%P;
    return ret;
}
int main(){
    int cmd,x,y,z,i;
    scanf("%d%d%d%d",&N,&M,&R,&P);
    for(i=1;i<=N;i++)scanf("%d",&v[i]);
    memset(head,0,sizeof(head)),cnt=0;
    for(i=1;i<N;i++)scanf("%d%d",&x,&y),add_edge(x,y),add_edge(y,x);//无向图
    memset(son,0,sizeof(son)),fa[R]=0,d[0]=0,dfs1(R);//此处错写成memset(son,0,sizeof(son)),fa[R]=0,d[R]=0,dfs1(R);
    cnt=0,dfs2(R,R);
    build(1,N,1);
    for(i=1;i<=M;i++){
        scanf("%d",&cmd);
        if(cmd==1)scanf("%d%d%d",&x,&y,&z),updates(x,y,z,1);
        else if(cmd==2)scanf("%d%d",&x,&y),printf("%d\n",sum(x,y,1));
        else if(cmd==3)scanf("%d%d",&x,&z),update(id[x],id[x]+size[x]-1,z,1);
        else if(cmd==4)scanf("%d",&x),printf("%d\n",query(id[x],id[x]+size[x]-1,1));
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值