洛谷3384
树链剖分:
fa[ ]父亲节点;son[ ]重儿子节点;siz[ ]以该点为根的子树的大小;
id[ ]该点在两次dfs后形成序列中的位置;dep[ ]该点深度;
wt[ ]两次dfs后形成的序列;top[ ]该点所在链的链头
1.dfs1先求出每个点的深度,父亲节点和以该点为根的子树的大小,并找到其重儿子(及siz最大的儿子)
2.dfs2求出最终形成的序列,并求出每个点所在链的链头(对于一个点,优先遍历重儿子,保证重链在序列中的连续)
3.对形成的序列建议可先段树
3.操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z
先将x,y整条链整条链
的往上跳,直到他们跳到同一条链上,过程中求出每次跳动区间的和,再求出同一条链中的他们之间的值(此时能够保证是最短路,否则他们之前必然能跳到同一条链上)
4.操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和,类似操作1
5.操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
一棵子树的点在序列中必然连续,又已知其siz,则该棵树上的点的区间为【id[x],id[x]+siz[x]-1】
6.操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
同操作3
#include<cstdio> #include<cctype> #include<algorithm> #include<cstring> #define Rint register int #define mid ((l+r)>>1) #define lson rt<<1,l,mid #define rson rt<<1|1,mid+1,r using namespace std; const int maxn=200005; int n,m,r,mo,ne,res=0,cnt,laz[maxn<<2],w[maxn],fa[maxn],siz[maxn],dep[maxn],tr[maxn<<2],top[maxn],wt[maxn],id[maxn],to[maxn],nex[maxn],head[maxn],son[maxn]; inline void add(int x,int y){ to[++ne]=y;nex[ne]=head[x];head[x]=ne; } inline void read(int &x){ char ch=getchar();x=0; while(!isdigit(ch))ch=getchar(); while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} } inline void pushdown(int rt,int len){ if(laz[rt]==0)return; laz[rt<<1]+=laz[rt];laz[rt<<1|1]+=laz[rt]; tr[rt<<1]+=laz[rt]*(len-(len>>1));tr[rt<<1|1]+=laz[rt]*(len>>1); tr[rt<<1]%=mo;tr[rt<<1|1]%=mo; laz[rt]=0; } inline void build(int rt,int l,int r){ if(l==r){tr[rt]=wt[l]%mo;return;} build(lson);build(rson); tr[rt]=(tr[rt<<1]+tr[rt<<1|1])%mo; } inline void query(int rt,int l,int r,int L,int R){ if(l>=L&&r<=R){res+=tr[rt];res%=mo;return;} pushdown(rt,r-l+1); if(mid>=L)query(lson,L,R); if(mid<R)query(rson,L,R); } inline void updata(int rt,int l,int r,int L,int R,int k){ if(l>=L&&r<=R){laz[rt]+=k;tr[rt]+=k*(r-l+1);return;} pushdown(rt,r-l+1); if(mid>=L)updata(lson,L,R,k); if(mid<R)updata(rson,L,R,k); tr[rt]=(tr[rt<<1]+tr[rt<<1|1])%mo; } inline int qRange(int x,int y){ int ans=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); res=0; query(1,1,n,id[top[x]],id[x]); ans+=res;ans%=mo;x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); res=0; query(1,1,n,id[x],id[y]); ans+=res; return ans%mo; } inline void updRange(int x,int y,int k){//同上 k%=mo; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); updata(1,1,n,id[top[x]],id[x],k); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); updata(1,1,n,id[x],id[y],k); } inline void updson(int x,int k){ updata(1,1,n,id[x],id[x]+siz[x]-1,k); } inline int qson(int x){ res=0; query(1,1,n,id[x],id[x]+siz[x]-1);return res; } inline void dfs1(int x,int f,int depth){ dep[x]=depth;fa[x]=f;siz[x]=1; int maxson=-1; for(Rint i=head[x];i;i=nex[i]){ int y=to[i]; if(y==f)continue; dfs1(y,x,depth+1); siz[x]+=siz[y]; if(siz[y]>maxson){son[x]=y;maxson=siz[y];} } } inline void dfs2(int x,int topf){ id[x]=++cnt; wt[cnt]=w[x]; top[x]=topf; if(!son[x])return; dfs2(son[x],topf); for(Rint i=head[x];i;i=nex[i]){ int y=to[i]; if(y==fa[x]||y==son[x])continue; dfs2(y,y); } } int main(){ read(n);read(m);read(r);read(mo); for(Rint i=1;i<=n;i++)read(w[i]); for(Rint i=1;i<n;i++){ int a,b;read(a);read(b); add(a,b);add(b,a); } dfs1(r,0,1);dfs2(r,r); build(1,1,n); while(m--){ int typ,x,y,z; read(typ);read(x); switch(typ){ case 1:read(y);read(z);updRange(x,y,z);break; case 2:read(y); printf("%d\n",qRange(x,y));break; case 3:read(y);updson(x,y);break; case 4:printf("%d\n",qson(x));break; } } }
树链剖分代码虽长,但思路非常清晰,思想也容易理解。