树链剖分模板

题目描述

如题,已知一棵包含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为根节点的子树内所有节点值之和

输入输出格式

输入格式:

第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。

接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)

接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:

操作1: 1 x y z

操作2: 2 x y

操作3: 3 x z

操作4: 4 x

输出格式:

输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模

输入输出样例

输入样例#1:
5 5 2 24
7 3 7 8 0 
1 2
1 5
3 1
4 1
3 4 2
3 2 2
4 5
1 5 1 3
2 1 3
输出样例#1:
2
21

说明

时空限制:1s,128M

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=1000,M<=1000

对于100%的数据:N<=100000,M<=100000

(其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233)

样例说明:

树的结构如下:

各个操作如下:

故输出应依次为2、21(重要的事情说三遍:记得取模)

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
const int maxx=100010;
int be[maxx],ne[maxx*2],to[maxx*2],e=0;
int fa[maxx],son[maxx],deep[maxx],size[maxx],jump[maxx],tot=0;
int x[maxx],y[maxx],a[maxx];
long long tree[maxx*4],lazy[maxx*4],val;
int n,q,root,mod;
void add(int x,int y){
    to[++e]=y;
    ne[e]=be[x];
    be[x]=e;
}
int read(){
    char x;
    while((x=getchar())<'0' || x>'9');
    int u=x-'0';
    while((x=getchar())>='0' && x<='9')u=u*10+x-'0';
    return u;
}
int dfs1(int id){
    size[id]=1;
    for(int i=be[id];i;i=ne[i]){
        int go=to[i];
        if(fa[id]==go)continue;
        fa[go]=id;
        deep[go]=deep[id]+1;
        size[id]+=dfs1(go);
        if(!son[id] || size[go]>size[son[id]])
            son[id]=go;
    }
    return size[id];
}
void dfs2(int id,int top){
    x[id]=++tot;
    y[tot]=id;
    jump[id]=top;
    if(son[id]) dfs2(son[id],top);
    for(int i=be[id];i;i=ne[i]){
        int go=to[i];
        if(fa[id]==go ||son[id]==go)continue;
        dfs2(go,go);
    }
}
void build(int id,int l,int r){
    if(l==r){
        tree[id]=a[y[l]]%mod;
//        if(l==4) printf("%d~%d\n",y[l],id);
        return ;
    }
    int mid=(l+r)>>1;
    build(id<<1,l,mid),build(id<<1|1,mid+1,r);
    tree[id]=(tree[id*2]+tree[id*2+1])%mod;
}
void update(int l,int r,int xl,int xr,int id,int v){
    if(l>=xl && r<=xr)    lazy[id]+=v;
    else{
        int mid=(l+r)>>1;
        if(xl>mid)update(mid+1,r,xl,xr,id<<1|1,v);
        else if(xr<=mid)update(l,mid,xl,xr,id<<1,v);
        else update(l,mid,xl,mid,id<<1,v),update(mid+1,r,mid+1,xr,id<<1|1,v);
    }
    if(l<r)tree[id]=(tree[id<<1]+tree[id<<1|1])%mod;
    if(l==r)tree[id]+=v;
    else tree[id]=(tree[id]+lazy[id]*(r-l+1))%mod;
}
void pushdown(int id,int l,int r,int mid){
	lazy[id<<1]=(lazy[id<<1]+lazy[id])%mod;
	lazy[id<<1|1]=(lazy[id<<1|1]+lazy[id])%mod;
	tree[id<<1]=(tree[id<<1]+(mid-l+1)*lazy[id])%mod;
	tree[id<<1|1]=(tree[id<<1|1]+(r-mid)*lazy[id])%mod;
	lazy[id]=0;
}
long long query(int l,int r,int cl,int cr,int id){
    if(l>=cl && r<=cr){
		return tree[id]%mod;
    }
    else{
        int mid=(l+r)>>1;
		if(lazy[id])pushdown(id,l,r,mid);
        if(cl>mid) return query(mid+1,r,cl,cr,id<<1|1)%mod;
        else if(cr<=mid) return query(l,mid,cl,cr,id<<1)%mod;
        else return (query(l,mid,cl,mid,id<<1)+query(mid+1,r,mid+1,cr,id<<1|1))%mod;
    }
}
void change(int a,int b,int w){
    while(jump[a]!=jump[b]){
        if(deep[jump[a]]>deep[jump[b]]){a^=b;b^=a;a^=b;}
        update(1,tot,x[jump[b]],x[b],1,w);
//        printf("%d~%d\n",x[jump[b]],x[b]);
        b=fa[jump[b]];
    }
    if(deep[a]>deep[b]){a^=b;b^=a;a^=b;}
    update(1,tot,x[a],x[b],1,w);
//    printf("%d~%d\n",x[jump[b]],x[b]);
}
void ask(int a,int b){
    while(jump[a]!=jump[b]){
        if(deep[jump[a]]>deep[jump[b]]){a^=b;b^=a;a^=b;}
        val=(val+query(1,tot,x[jump[b]],x[b],1))%mod;
        b=fa[jump[b]];
    }
    if(deep[a]>deep[b]){a^=b;b^=a;a^=b;}
        val=(val+query(1,tot,x[a],x[b],1))%mod;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
    freopen("output.out","w",stdout);
#endif 
    int i,j,k;
    n=read();
    q=read();
    root=read();
    mod=read();
    for(i=1;i<=n;i++){a[i]=read();a[i]%=mod;}
    for(i=1;i<n;i++){
        j=read();
        k=read();
        add(j,k);
        add(k,j);
    }
    fa[root]=root;
    dfs1(root);
    dfs2(root,root);
    build(1,1,tot);    
//    for(i=1;i<=n;i++)
//        printf("%d~%d\n",3,query(1,tot,x[3],x[3],1,0));
    while(q--){    
        int dog=read();        
//        printf("-------%d---------\n",dog);
        if(dog==1){
            i=read();
            j=read();
            val=read();
            val%=mod;
            change(i,j,val);
        }
        else if(dog==3){
            i=read();
            val=read();
            val%=mod;
            update(1,tot,x[i],x[i]+size[i]-1,1,val);
        }
        else if(dog==2){
            i=read();
            j=read();
            val=0;
            ask(i,j);
            printf("%d\n",val%mod);
        }
        else{
            i=read();
        //    printf("%d\n",x[i]);
            printf("%d\n",query(1,tot,x[i],x[i]+size[i]-1,1)%mod);
        }
//        printf("-------%d---------\n",q);
//        for(i=1;i<=n;i++)    printf("%d~%d\n",i,query(1,tot,x[i],x[i],1,0));
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值