[模板]树链剖分
题目描述
如题,已知一棵包含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 }
来自一只刚参加过NOIP的蒟蒻