[luogu 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取模)
输入输出样例
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
2 21
说明
时空限制:1s,128M
数据规模:
对于30%的数据: N \leq 10, M \leq 10N≤10,M≤10
对于70%的数据: N \leq {10}^3, M \leq {10}^3N≤103,M≤103
对于100%的数据: N \leq {10}^5, M \leq {10}^5N≤105,M≤105
( 其实,纯随机生成的树LCA+暴力是能过的,可是,你觉得可能是纯随机的么233 )
样例说明:
树的结构如下:
各个操作如下:
故输出应依次为2、21(重要的事情说三遍:记得取模)
树剖模板题啊。。。都快不会打树剖了。
显然,对于这种题目,就要将每一个点根据dfs序映射到线性表上,然后用线段树等维护。
对于1,2操作,就是树剖的基本修改-查询操作;
对于3,4操作,也就是在x的子树里修改查询。那么,一个子树内部的点在dfs序对应的线性表上是连续的,也就可以通过线段树维护了。
ps:树剖都是码农题。。不过打得倒还挺爽。
code:
%:pragma GCC optimize() #include<bits/stdc++.h> #define mid (((l)+(r))>>1) #define amod(x,y) (((x)+=(y))%=T) #define LL long long #define IL inline using namespace std; ; ],son[N<<]; int dep[N],fa[N],si[N],down[N],top[N],bel[N],idl[N],idr[N]; LL v[N],v0[N],a[N<<],t[N<<],T; IL LL read() { LL x=; char ch=getchar(); ') ch=getchar(); +ch-',ch=getchar(); return x; } IL void add(int x,int y) {nxt[++tot]=lnk[x],lnk[x]=tot,son[tot]=y;} IL void dfs(int x,int p) { dep[x]=dep[p]+,fa[x]=p,si[x]=,down[x]=n+; for (int j=lnk[x]; j; j=nxt[j]) if (son[j]!=p) { dfs(son[j],x),si[x]+=si[son[j]]; down[x]=(si[down[x]]<si[son[j]])?son[j]:down[x]; } } IL void pre(int x,int p) { idl[x]=++cloc; ) top[down[x]]=top[x],bel[down[x]]=bel[x],pre(down[x],x); for (int j=lnk[x]; j; j=nxt[j]) if (son[j]!=p&&son[j]!=down[x]) { top[son[j]]=son[j],bel[son[j]]=++chain,pre(son[j],x); } idr[x]=cloc; } IL LL am(LL x,LL y) {return (x+y)%T;} IL ],a[c<<|]);} IL void pushD(int c,int l,int r) { amod(t[c<<],t[c]); amod(t[c<<|],t[c]); amod(a[c<<],t[c]*(LL)(mid-l+)); amod(a[c<<|],t[c]*(LL)(r-mid)); t[c]=; } IL void B(int c,int l,int r) { if (l==r) {a[c]=v0[l]%T; return;} B(c<<,l,mid),B(c<<|,mid+,r); pushU(c); } IL void U(int c,int l,int r,LL v,int liml,int limr) { )),pushD(c,l,r); return;} pushD(c,l,r); ,l,mid,v,liml,limr); |,mid+,r,v,liml,limr); ,l,mid,v,liml,mid),U(c<<|,mid+,r,v,mid+,limr); pushU(c); } IL LL A(int c,int l,int r,int liml,int limr) { if (l>=liml&&r<=limr) return a[c]%T; pushD(c,l,r); LL ret=; ,l,mid,liml,limr)); |,mid+,r,liml,limr)); ,l,mid,liml,mid)),amod(ret,A(c<<|,mid+,r,mid+,limr)); pushU(c); return ret%T; } IL void chain_U(int x,int y,LL v) { while (bel[x]!=bel[y]) { ,,n,v,idl[top[x]],idl[x]),x=fa[top[x]]; ,,n,v,idl[top[y]],idl[y]),y=fa[top[y]]; } if (dep[x]>dep[y]) swap(x,y); U(,,n,v,idl[x],idl[y]); } IL LL chain_A(int x,int y) { LL ret=; while (bel[x]!=bel[y]) { ,,n,idl[top[x]],idl[x])),x=fa[top[x]]; ,,n,idl[top[y]],idl[y])),y=fa[top[y]]; } if (dep[x]>dep[y]) swap(x,y); amod(ret,A(,,n,idl[x],idl[y])); return ret%T; } IL ,,n,v,idl[x],idr[x]);} IL LL tree_A(,,n,idl[x],idr[x])%T;} int main() { n=read(),m=read(),r=read(),T=read(); ; i<=n; i++) v[i]=read(); ,x,y; i<n; i++) x=read(),y=read(),add(x,y),add(y,x); dep[]=,si[n+]=,dfs(r,),pre(r,); ; i<=n; i++) v0[idl[i]]=v[i]; B(,,n); for (int o,x,y,z; m; m--) { o=read(),x=read(),y=(o<)?read():,z=(o&)?read():; switch (o) { :chain_U(x,y,(LL)z%T); break; :printf("%lld\n",chain_A(x,y)%T); break; :tree_U(x,(LL)z%T); break; :printf("%lld\n",tree_A(x)%T); break; } } ; }