P3384 【模板】树链剖分
题解
题目描述
如题,已知一棵包含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≤10^3,M≤10^3
对于100%的数据: N≤10^5,M≤10^5
( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )
样例说明:
树的结构如下:
各个操作如下:
故输出应依次为2、21(重要的事情说三遍:记得取模)
模板般而已
1 #include <vector> 2 #include <cstdio> 3 #include <cctype> 4 5 const int MAXN=100010; 6 7 int n,m,r,p,inr; 8 9 int a[MAXN],son[MAXN],id[MAXN],fa[MAXN],top[MAXN],dep[MAXN],siz[MAXN],rank[MAXN]; 10 11 std::vector<int> Graph[MAXN]; 12 13 inline void read(int&x) { 14 int f=1;register char c=getchar(); 15 for(x=0;!isdigit(c);c=='-'&&(f=-1),c=getchar()); 16 for(;isdigit(c);x=x*10+c-48,c=getchar()); 17 x=x*f; 18 } 19 20 struct node { 21 int l,r; 22 int sum,tag; 23 }; 24 node t[MAXN<<2]; 25 26 void DFS_1(int u,int f) { 27 dep[u]=dep[f]+1; 28 siz[u]=1; 29 fa[u]=f; 30 for(int i=0;i<Graph[u].size();++i) { 31 int v=Graph[u][i]; 32 if(v==f) continue; 33 DFS_1(v,u); 34 siz[u]+=siz[v]; 35 if(siz[son[u]]<siz[v]) son[u]=v; 36 } 37 } 38 39 void DFS_2(int u,int tp) { 40 id[u]=++inr; 41 rank[inr]=a[u]; 42 top[u]=tp; 43 if(son[u]==-1) return; 44 if(son[u]) DFS_2(son[u],tp); 45 for(int i=0;i<Graph[u].size();++i) { 46 int v=Graph[u][i]; 47 if(v==son[u]||v==fa[u]) continue; 48 DFS_2(v,v); 49 } 50 } 51 52 void build_tree(int now,int l,int r) { 53 t[now].l=l;t[now].r=r; 54 if(l==r) { 55 t[now].sum=rank[l]; 56 t[now].tag=0; 57 return; 58 } 59 int mid=(l+r)>>1; 60 build_tree(now<<1,l,mid); 61 build_tree(now<<1|1,mid+1,r); 62 t[now].sum=(t[now<<1].sum+t[now<<1|1].sum)%p; 63 } 64 65 void down(int now) { 66 t[now<<1].tag+=t[now].tag; 67 t[now<<1|1].tag+=t[now].tag; 68 t[now<<1].sum=(t[now<<1].sum+(t[now<<1].r-t[now<<1].l+1)*t[now].tag)%p; 69 t[now<<1|1].sum=(t[now<<1|1].sum+(t[now<<1|1].r-t[now<<1|1].l+1)*t[now].tag)%p; 70 t[now].tag=0; 71 } 72 73 void modify(int now,int l,int r,int v) { 74 if(l<=t[now].l&&r>=t[now].r) { 75 t[now].sum+=(t[now].r-t[now].l+1)*v%p; 76 t[now].tag+=v; 77 return; 78 } 79 if(t[now].tag) down(now); 80 int mid=(t[now].l+t[now].r)>>1; 81 if(l<=mid) modify(now<<1,l,r,v); 82 if(r>mid) modify(now<<1|1,l,r,v); 83 t[now].sum=(t[now<<1].sum+t[now<<1|1].sum)%p; 84 } 85 86 int query(int now,int l,int r) { 87 int ans=0; 88 if(l<=t[now].l&&r>=t[now].r) return t[now].sum%p; 89 if(t[now].tag) down(now); 90 int mid=(t[now].l+t[now].r)>>1; 91 if(l<=mid) ans=(ans+query(now<<1,l,r))%p; 92 if(r>mid) ans=(ans+query(now<<1|1,l,r))%p; 93 return ans; 94 } 95 96 int Pre(int x,int y,int z) { 97 int ans=0; 98 while(top[x]!=top[y]) { 99 if(dep[top[x]]<dep[top[y]]) x^=y^=x^=y; 100 if(z) modify(1,id[top[x]],id[x],z); 101 else ans=(ans+query(1,id[top[x]],id[x]))%p; 102 x=fa[top[x]]; 103 } 104 if(dep[x]>dep[y]) x^=y^=x^=y; 105 if(z) modify(1,id[x],id[y],z); 106 else ans=(ans+query(1,id[x],id[y]))%p; 107 if(!z) return ans; 108 } 109 110 int hh() { 111 read(n);read(m);read(r);read(p); 112 for(int i=1;i<=n;++i) read(a[i]),son[i]=-1; 113 for(int x,y,i=1;i<n;++i) { 114 read(x);read(y); 115 Graph[x].push_back(y); 116 Graph[y].push_back(x); 117 } 118 DFS_1(r,0);DFS_2(r,r); 119 build_tree(1,1,inr); 120 for(int flag,x,y,z,i=1;i<=m;++i) { 121 read(flag);read(x); 122 if(flag==1) { 123 read(y);read(z); 124 Pre(x,y,z); 125 } 126 else if(flag==2) { 127 read(y); 128 int ans=Pre(x,y,0); 129 printf("%d\n",ans); 130 } 131 else if(flag==3) { 132 read(z); 133 modify(1,id[x],siz[x]+id[x]-1,z); 134 } 135 else { 136 int ans=query(1,id[x],siz[x]+id[x]-1); 137 printf("%d\n",ans); 138 } 139 } 140 return 0; 141 } 142 143 int sb=hh(); 144 int main(int argc,char**argv) {;}