[模板]树链剖分

题目描述

如题,已知一棵包含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 #include<cstdio>
  2 #define ll long long
  3 using namespace std;
  4 const int maxn=1e5+5;
  5 int v[maxn],head[maxn];
  6 int mod,num,ord;
  7 struct Edge{
  8     int to,next;
  9 }edge[maxn<<1];
 10 struct Tree{
 11     int v,lazy; 
 12 }tree[maxn<<2];
 13 struct cp{
 14     int v,size,son,dep,ord,top,fa;
 15 }e[maxn];
 16 inline void add(int u,int v){
 17     edge[++num].to=v;
 18     edge[num].next=head[u];
 19     head[u]=num;
 20 }
 21 void build(int x,int s,int t){
 22     if(s==t) tree[x].v=v[s];
 23     else{
 24         int mid=s+t>>1;
 25         build(x<<1,s,mid);
 26         build(x<<1|1,mid+1,t);
 27         tree[x].v=tree[x<<1].v+tree[x<<1|1].v;
 28     }
 29 }
 30 void swap(int &x,int &y){
 31     x^=y^=x^=y;
 32 }
 33 void dfs1(int x){
 34     int size=0;
 35     e[x].size=1;
 36     for(int i=head[x];i;i=edge[i].next){
 37         int v=edge[i].to;
 38         if(v==e[x].fa) continue;
 39         e[v].fa=x;
 40         e[v].dep=e[x].dep+1;
 41         dfs1(v);
 42         e[x].size+=e[v].size;
 43         if(e[v].size>size) size=e[v].size,e[x].son=v;
 44     }
 45 }
 46 void dfs2(int x){
 47     e[x].ord=++ord;//ord为dfs序; 
 48     v[ord]=e[x].v;
 49     if(e[e[x].fa].son!=x) e[x].top=x;
 50     else e[x].top=e[e[x].fa].top;
 51     if(e[x].son) dfs2(e[x].son);
 52     for(int i=head[x];i;i=edge[i].next)
 53         if(e[x].fa!=edge[i].to&&e[x].son!=edge[i].to) dfs2(edge[i].to);
 54 }
 55 inline void pushdown(int x,int s,int t){
 56     if(!tree[x].lazy) return ;
 57     int mid=s+t>>1;
 58     tree[x<<1].lazy+=tree[x].lazy;
 59     tree[x<<1|1].lazy+=tree[x].lazy;
 60     tree[x<<1].v=((ll)(tree[x<<1].v+tree[x].lazy*(mid-s+1)))%mod;
 61     tree[x<<1|1].v=((ll)(tree[x<<1|1].v+tree[x].lazy*(t-mid)))%mod;
 62     tree[x].lazy=0;
 63 }
 64 void update(int x,int ns,int nt,int s,int t,int add){
 65     if(nt<s||ns>t) return ;
 66     if(ns>=s&&nt<=t){
 67         tree[x].lazy+=add;
 68         tree[x].v=((ll)tree[x].v+add*(nt-ns+1))%mod;
 69         return ;
 70     }
 71     pushdown(x,ns,nt);
 72     int mid=ns+nt>>1;
 73     update(x<<1,ns,mid,s,t,add);
 74     update(x<<1|1,mid+1,nt,s,t,add);
 75     tree[x].v=(tree[x<<1].v+tree[x<<1|1].v)%mod;
 76 }
 77 int query(int x,int ns,int nt,int s,int t){
 78     if(nt<s||ns>t) return 0;
 79     if(ns>=s&&nt<=t) return tree[x].v;
 80     pushdown(x,ns,nt);
 81     int mid=ns+nt>>1;
 82     return (query(x<<1,ns,mid,s,t)+query(x<<1|1,mid+1,nt,s,t))%mod;
 83 }
 84 inline int read(){
 85     char ch=getchar();
 86     int x=0,f=1;
 87     while(ch<'0'||ch>'9'){
 88         if(ch=='-') f=-1;
 89         ch=getchar();
 90     }
 91     while(ch>='0'&&ch<='9'){
 92         x=(x<<1)+(x<<3)+(ch^48);
 93         ch=getchar();
 94     }
 95     return x*f;
 96 }
 97 int main(){
 98     int n,m,r,x,y,z,f;
 99     n=read(),m=read(),r=read(),mod=read();
100     for(int i=1;i<=n;i++) e[i].v=read();
101     for(int i=1;i<n;i++){
102         x=read(),y=read();
103         add(x,y),add(y,x);
104     }
105     dfs1(r),dfs2(r);
106     build(1,1,n);
107     while(m--){
108         f=read(),x=read();
109         if(f==1){//表示将树从x到y结点最短路径上所有节点的值都加上z
110             y=read(),z=read();
111             while(e[x].top!=e[y].top){
112                 if(e[e[x].top].dep<e[e[y].top].dep) swap(x,y);
113                 update(1,1,n,e[e[x].top].ord,e[x].ord,z);
114                 x=e[e[x].top].fa;
115             }
116             if(e[x].ord>e[y].ord) swap(x,y);
117             update(1,1,n,e[x].ord,e[y].ord,z);
118         }
119         if(f==2){//表示求树从x到y结点最短路径上所有节点的值之和
120             int s=0;
121             y=read();
122             while(e[x].top!=e[y].top){
123                 if(e[e[x].top].dep<e[e[y].top].dep) swap(x,y);
124                 s+=query(1,1,n,e[e[x].top].ord,e[x].ord);
125                 x=e[e[x].top].fa;
126             }
127             if(e[x].ord>e[y].ord) swap(x,y);
128             printf("%d\n",(s+query(1,1,n,e[x].ord,e[y].ord))%mod);
129         }
130         if(f==3){//表示将以x为根节点的子树内所有节点值都加上z
131             y=read();
132             update(1,1,n,e[x].ord,e[x].ord+e[x].size-1,y);
133         }
134         if(f==4)//表示求以x为根节点的子树内所有节点值之和
135             printf("%d\n",query(1,1,n,e[x].ord,e[x].ord+e[x].size-1));
136     }
137     return 0;
138 }
树链剖分

猜你喜欢

转载自www.cnblogs.com/RisingGods/p/9126421.html