树链剖分
Posted downrainsun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树链剖分相关的知识,希望对你有一定的参考价值。
https://www.cnblogs.com/chinhhh/p/7965433.html#dfs1
树链剖分跳链logn的复杂度。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N =200000+10; vector<int> g[N]; int w[N],wt[N]; //w[]初始的、wt[]新的点权数组 int a[N<<2],laz[N<<2]; //线段树数组、lazy操作 int son[N],id[N],fa[N],cnt,dep[N],siz[N],top[N]; //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点 int res; //查询答案 int n,m,r,mod; //-------------------------------------- 以下为线段树 inline void pushdown(int rt,int len) if(laz[rt]) laz[rt<<1]+=laz[rt]; laz[rt<<1|1]+=laz[rt]; a[rt<<1]+=laz[rt]*(len-(len>>1)); a[rt<<1|1]+=laz[rt]*(len>>1); a[rt<<1]%=mod; a[rt<<1|1]%=mod; laz[rt]=0; void pushup(int rt) a[rt]=(a[rt<<1]+a[rt<<1|1])%mod; void build(int rt,int l,int r) if(l==r) a[rt]=wt[l]; if(a[rt]>mod)a[rt]%=mod; return; int m = (l + r) >> 1; build(rt << 1, l, m); build(rt << 1 | 1, m + 1, r); pushup(rt); void query(int rt,int l,int r,int le,int re) if(le<=l&&r<=re) res+=a[rt]; res%=mod; return; pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) query(rt << 1, l, m, le, re); else if(le > m) query(rt << 1 | 1, m + 1, r, le, re); else query(rt << 1, l, m, le, m); query(rt << 1 | 1, m + 1, r, m + 1, re); void update(int rt,int l,int r,int le,int re,int k) if(le<=l&&re >= r) laz[rt]+=k; a[rt]+=k*(r - l + 1); if(a[rt] > mod) a[rt] %= mod; return; pushdown(rt,r - l + 1); int m = (l + r) >> 1; if(re <= m) update(rt << 1, l, m, le, re, k); else if(le > m) update(rt << 1 | 1, m + 1, r, le, re, k); else update(rt << 1, l, m, le, m, k); update(rt << 1 | 1, m + 1, r, m + 1, re, k); pushup(rt); //---------------------------------以上为线段树 int qRange(int x,int y) int ans=0; while(top[x]!=top[y])//当两个点不在同一条链上 if(dep[top[x]]<dep[top[y]])swap(x,y);//把x点改为所在链顶端的深度更深的那个点 res=0; query(1,1,n,id[top[x]],id[x]);//ans加上x点到x所在链顶端 这一段区间的点权和 ans+=res; ans%=mod;//按题意取模 x=fa[top[x]];//把x跳到x所在链顶端的那个点的上面一个点 //直到两个点处于一条链上 if(dep[x]>dep[y])swap(x,y);//把x点深度更深的那个点 res=0; query(1,1,n,id[x],id[y]);//这时再加上此时两个点的区间和即可 ans+=res; return ans%mod; void updRange(int x,int y,int k)//同上 k%=mod; while(top[x]!=top[y]) if(dep[top[x]]<dep[top[y]])swap(x,y); update(1,1,n,id[top[x]],id[x],k); x=fa[top[x]]; if(dep[x]>dep[y])swap(x,y); update(1,1,n,id[x],id[y],k); int qSon(int x) res=0; query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 return res; void updSon(int x,int k)//同上 k %= mod; update(1,1,n,id[x],id[x]+siz[x]-1,k); void dfs1(int x,int f,int deep)//x当前节点,f父亲,deep深度 dep[x]=deep;//标记每个点的深度 fa[x]=f;//标记每个点的父亲 siz[x]=1;//标记每个非叶子节点的子树大小 int maxson=-1;//记录重儿子的儿子数 int len = g[x].size(); for(int i = 0; i < len; i++) int y=g[x][i]; if(y==f)continue;//若为父亲则continue dfs1(y,x,deep+1);//dfs其儿子 siz[x]+=siz[y];//把它的儿子数加到它身上 if(siz[y]>maxson)son[x]=y,maxson=siz[y];//标记每个非叶子节点的重儿子编号 void dfs2(int x,int topf)//x当前节点,topf当前链的最顶端的节点 id[x]=++cnt;//标记每个点的新编号 wt[cnt]=w[x];//把每个点的初始值赋到新编号上来 top[x]=topf;//这个点所在链的顶端 if(!son[x])return;//如果没有儿子则返回 dfs2(son[x],topf);//按先处理重儿子,再处理轻儿子的顺序递归处理 int len = g[x].size(); for(int i = 0; i < len; i++) int y=g[x][i]; if(y==fa[x]||y==son[x])continue; dfs2(y,y);//对于每一个轻儿子都有一条从它自己开始的链 int main() scanf("%d%d%d%d", &n, &m, &r, &mod); for(int i=1;i<=n;i++) scanf("%d", &w[i]); int u, v; for(int i=1;i<n;i++) scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); cnt = 0; dfs1(r,0,1); dfs2(r,r); build(1,1,n); while(m--) int k,x,y,z; scanf("%d", &k); if(k==1) scanf("%d%d%d", &x, &y, &z); updRange(x,y,z); else if(k==2) scanf("%d%d", &x, &y); printf("%d\\n",qRange(x,y)); else if(k==3) scanf("%d%d", &x, &y); updSon(x,y); else scanf("%d", &x); printf("%d\\n",qSon(x));
以上是关于树链剖分的主要内容,如果未能解决你的问题,请参考以下文章