树链剖分模板
摘要:
本以为自己学会了树剖,但最后发现我还是只会用树剖求lca,至于最后的线段树。。。
完全没有用到top,fa,son数组!
也就是说我学的树剖是错的!
所以赶紧来补一波板子:
题目:
这是一道模板题。
给定一棵n个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:
-
换根:将一个指定的节点设置为树的新根。
-
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
-
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
-
询问路径:询问某条路径上节点的权值和。
-
询问子树:询问某个子树内节点的权值和。
输入格式
第一行一个整数n,表示节点的个数。
第二行n个整数表示第i个节点的初始权值$a_i$。
第三行n-1个整数,表示i+1号节点的父节点编号$f_{i+1}(1\leqslant f_{i+1}\leqslant n)$。
第四行一个整数m,表示操作个数。
接下来m行,每行第一个整数表示操作类型编号:$1\leqslant u,v\leqslant n$
若类型为1,则接下来一个整数u,表示新根的编号。
若类型为2,则接下来三个整数u,v,k,分别表示路径两端的节点编号以及增加的权值。
若类型为3,则接下来两个整数u,k,分别表示子树根节点编号以及增加的权值。
若类型为4,则接下来两个整数u,v,表示路径两端的节点编号。
若类型为5,则接下来一个整数u,表示子树根节点编号。
输出格式
对于每一个类型为4或5的操作,输出一行一个整数表示答案。
样例
样例输入
6
1 2 3 4 5 6
1 2 1 4 4
6
4 5 6
2 2 4 1
5 1
1 4
3 1 2
4 2 5
样例输出
15
24
19
数据范围与提示
对于 100% 的数据,$1\leqslant n,m,k,a_i\leqslant 10^5$
数据有一定梯度。
有不同板子:
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #define Rint register int #define mem(a,b) memset(a,(b),sizeof(a)) #define Temp template<typename T> using namespace std; typedef long long LL; Temp inline void read(T &x){ x=0;T w=1,ch=getchar(); while(!isdigit(ch)&&ch!='-')ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); x=x*w; } #define mid ((l+r)>>1) #define lson rt<<1,l,mid #define rson rt<<1|1,mid+1,r #define len (r-l+1) const int maxn=200000+10; int n,m,r,mod; //见题意 int e,beg[maxn],nex[maxn],to[maxn],w[maxn],wt[maxn]; //链式前向星数组,w[]、wt[]初始点权数组 int a[maxn<<2],laz[maxn<<2]; //线段树数组、lazy操作 int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn]; //son[]重儿子编号,id[]新编号,fa[]父亲节点,cnt dfs_clock/dfs序,dep[]深度,siz[]子树大小,top[]当前链顶端节点 int res=0; //查询答案 inline void add(int x,int y){//链式前向星加边 to[++e]=y; nex[e]=beg[x]; beg[x]=e; } //-------------------------------------- 以下为线段树 inline void pushdown(int rt,int lenn){ laz[rt<<1]+=laz[rt]; laz[rt<<1|1]+=laz[rt]; a[rt<<1]+=laz[rt]*(lenn-(lenn>>1)); a[rt<<1|1]+=laz[rt]*(lenn>>1); a[rt<<1]%=mod; a[rt<<1|1]%=mod; laz[rt]=0; } inline void build(int rt,int l,int r){ if(l==r){ a[rt]=wt[l]; if(a[rt]>mod)a[rt]%=mod; return; } build(lson); build(rson); a[rt]=(a[rt<<1]+a[rt<<1|1])%mod; } inline void query(int rt,int l,int r,int L,int R){ if(L<=l&&r<=R){res+=a[rt];res%=mod;return;} else{ if(laz[rt])pushdown(rt,len); if(L<=mid)query(lson,L,R); if(R>mid)query(rson,L,R); } } inline void update(int rt,int l,int r,int L,int R,int k){ if(L<=l&&r<=R){ laz[rt]+=k; a[rt]+=k*len; } else{ if(laz[rt])pushdown(rt,len); if(L<=mid)update(lson,L,R,k); if(R>mid)update(rson,L,R,k); a[rt]=(a[rt<<1]+a[rt<<1|1])%mod; } } //---------------------------------以上为线段树 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);//把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; } inline 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); } inline int qSon(int x){ res=0; query(1,1,n,id[x],id[x]+siz[x]-1);//子树区间右端点为id[x]+siz[x]-1 return res; } inline void updSon(int x,int k){//同上 update(1,1,n,id[x],id[x]+siz[x]-1,k); } inline void dfs1(int x,int f,int deep){//x当前节点,f父亲,deep深度 dep[x]=deep;//标记每个点的深度 fa[x]=f;//标记每个点的父亲 siz[x]=1;//标记每个非叶子节点的子树大小 int maxson=-1;//记录重儿子的儿子数 for(Rint i=beg[x];i;i=nex[i]){ int y=to[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];//标记每个非叶子节点的重儿子编号 } } inline 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);//按先处理重儿子,再处理轻儿子的顺序递归处理 for(Rint i=beg[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(mod); 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 k,x,y,z; read(k); if(k==1){ read(x);read(y);read(z); updRange(x,y,z); } else if(k==2){ read(x);read(y); printf("%d\n",qRange(x,y)); } else if(k==3){ read(x);read(y); updSon(x,y); } else{ read(x); printf("%d\n",qSon(x)); } } }
还有个链接:https://www.cnblogs.com/chinhhh/p/7965433.html#build
这个:(倍增)
#include<cstdio> #include<vector> #include<algorithm> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define ll long long using namespace std; const int N=1e5+5; int son[N],siz[N],dep[N],top[N],idx[N],rev[N],num; int lg[N],f[N][20]; ll sum[N<<2],add[N<<2]; int n,q,op,a,b,c,w[N],root=1; vector<int>G[N]; struct TREE{ inline void pushup(int rt) {sum[rt]=sum[rt<<1]+sum[rt<<1|1];} inline void build(int l,int r,int rt) { add[rt]=0; if(l==r) {sum[rt]=w[rev[l]];return;} int m=(l+r)>>1; build(lson),build(rson); pushup(rt); } inline void pushdown(int rt,int m) { if(add[rt]) { add[rt<<1]+=add[rt],add[rt<<1|1]+=add[rt]; sum[rt<<1]+=add[rt]*(m-(m>>1)); sum[rt<<1|1]+=add[rt]*(m>>1); add[rt]=0; } } inline void update(int L,int R,int val,int l,int r,int rt) { if(L<=l && r<=R) { sum[rt]+=(ll)val*(r-l+1); add[rt]+=val; return; } pushdown(rt,r-l+1); int m=(l+r)>>1; if(L<=m) update(L,R,val,lson); if(R>m) update(L,R,val,rson); pushup(rt); } inline ll query(int L,int R,int l,int r,int rt) { if(L<=l && r<=R) return sum[rt]; pushdown(rt,r-l+1); int m=(l+r)>>1;ll ans=0; if(L<=m) ans+=query(L,R,lson); if(R>m) ans+=query(L,R,rson); return ans; } }T; struct Tree{ inline void dfs1(int x) { dep[x]=dep[f[x][0]]+1,siz[x]=1; for(int i=1;(1<<i)<=dep[x];++i) f[x][i]=f[f[x][i-1]][i-1]; for(int i=0;i<(int)G[x].size();++i) { int v=G[x][i]; if(v==f[x][0]) continue; dfs1(v),siz[x]+=siz[v]; if(!son[x] || siz[v]>siz[son[x]]) son[x]=v; } } inline void dfs2(int x,int t) { top[x]=t,idx[x]=++num,rev[num]=x; if(son[x]) dfs2(son[x],t); for(int i=0;i<(int)G[x].size();++i) { int v=G[x][i]; if(v==f[x][0] || v==son[x]) continue; dfs2(v,v); } } inline int lca(int x, int y) { if(dep[x]<dep[y]) swap(x, y); for(int i=18;i>=0;--i) if(dep[f[x][i]]>dep[y]) x=f[x][i]; if(f[x][0]==y) return x; if(dep[f[x][0]]>=dep[y]) x=f[x][0]; for(int i=lg[dep[x]]-1;i>=0;--i) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i]; return x; } inline void update_subtree(int x,int z) { if(x==root) {T.update(1,n,z,1,n,1);return;} int d=lca(x,root); // printf("test:%d %d %d\n",x,root,d); if(f[d][0]==x) T.update(1,n,z,1,n,1),T.update(idx[d],idx[d]+siz[d]-1,-z,1,n,1); else T.update(idx[x],idx[x]+siz[x]-1,z,1,n,1); } inline ll query_subtree(int x) { if(x==root) return T.query(1,n,1,n,1); int d=lca(x,root); // printf("test2:%d %d %d\n",x,root,d); if(f[d][0]==x) return T.query(1,n,1,n,1)-T.query(idx[d],idx[d]+siz[d]-1,1,n,1); else return T.query(idx[x],idx[x]+siz[x]-1,1,n,1); } inline void update_path(int x,int y,int z) { while(top[x]^top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); T.update(idx[top[x]],idx[x],z,1,n,1); x=f[top[x]][0]; } if(dep[x]>dep[y]) swap(x,y); T.update(idx[x],idx[y],z,1,n,1); } inline ll query_path(int x,int y) { ll ans=0; while(top[x]^top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); ans+=T.query(idx[top[x]],idx[x],1,n,1); x=f[top[x]][0]; } if(dep[x]>dep[y]) swap(x,y); ans+=T.query(idx[x],idx[y],1,n,1); return ans; } }t; int main() { // freopen("tree1.in","r",stdin); // freopen("tree.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&w[i]),lg[i]=lg[i>>1]+1; for(int i=2;i<=n;++i) scanf("%d",&f[i][0]), G[f[i][0]].push_back(i),G[i].push_back(f[i][0]); scanf("%d",&q); t.dfs1(1),t.dfs2(1,1),T.build(1,n,1); while(q--) { scanf("%d",&op); if(op==1) { scanf("%d",&a); root=a; } if(op==2) { scanf("%d%d%d",&a,&b,&c); t.update_path(a,b,c); } if(op==3) { scanf("%d%d",&a,&b); t.update_subtree(a,b); } if(op==4) { scanf("%d%d",&a,&b); printf("%lld\n",t.query_path(a,b)); } if(op==5) { scanf("%d",&a); printf("%lld\n",t.query_subtree(a)); } // printf("root:%d\n",root); } return 0; }
还有:(努力想找一个和自己码风相似的)
#include <cstdio> #include <algorithm> using namespace std; #define lson(x) (x << 1) #define rson(x) (x << 1 | 1) #define int long long const int maxn = 500050; struct Edge { int to, nxt; } edge[maxn << 1]; struct point { int val, add; } e[maxn << 2]; int n, q, root = 1, ecnt, cnt; int head[maxn], a[maxn], b[maxn]; int par[maxn], size[maxn], dep[maxn], son[maxn], id[maxn], top[maxn]; namespace segment_tree { void build(int x, int l, int r) { e[x].add = 0; if (l == r) e[x].val = a[l]; else { int mid = (l + r) >> 1; build(lson(x), l, mid); build(rson(x), mid + 1, r); e[x].val = e[lson(x)].val + e[rson(x)].val; } } void pushdown(int x, int l, int r) { int mid = (l + r) >> 1; e[lson(x)].val += (mid - l + 1) * e[x].add; e[rson(x)].val += (r - mid) * e[x].add; e[lson(x)].add += e[x].add; e[rson(x)].add += e[x].add; e[x].add = 0; } void update_addtag(int x, int stdl, int stdr, int l, int r, int k) { if (r < stdl || stdr < l) return; if (l <= stdl && stdr <= r) { e[x].val += k * (stdr - stdl + 1); e[x].add += k; return; } pushdown(x, stdl, stdr); int mid = (stdl + stdr) >> 1; update_addtag(lson(x), stdl, mid, l, r, k); update_addtag(rson(x), mid + 1, stdr, l, r, k); e[x].val = e[lson(x)].val + e[rson(x)].val; } int query(int x, int stdl, int stdr, int l, int r) { if (r < stdl || stdr < l) return 0; if (l <= stdl && stdr <= r) return e[x].val; pushdown(x, stdl, stdr); int mid = (stdl + stdr) >> 1; return query(lson(x), stdl, mid, l, r) + query(rson(x), mid + 1, stdr, l, r); } } // namespace segment_tree using namespace segment_tree; namespace hcp { void add(int x, int y) { edge[++ecnt].to = y; edge[ecnt].nxt = head[x]; head[x] = ecnt; } void dfs1(int x) { size[x] = 1; dep[x] = dep[par[x]] + 1; for (int i = head[x]; i; i = edge[i].nxt) { int t = edge[i].to; if (t == par[x]) continue; par[t] = x; dfs1(t); size[x] += size[t]; if (!son[x] || size[son[x]] < size[t]) son[x] = t; } } void dfs2(int x, int y) { top[x] = y; id[x] = ++cnt, a[cnt] = b[x]; if (son[x]) dfs2(son[x], y); for (int i = head[x]; i; i = edge[i].nxt) { int t = edge[i].to; if (t == par[x] || t == son[x]) continue; dfs2(t, t); } } int lca(int x, int y) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x, y); x = par[top[x]]; } if (dep[x] > dep[y]) swap(x, y); return x; } int findson(int x, int root) { while (top[root] != top[x]) { if (par[top[root]] == x) return top[root]; root = par[top[root]]; } return son[x]; } } // namespace hcp using namespace hcp; namespace solve_all { void update_path(int x, int y, int val) { while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x, y); update_addtag(1, 1, n, id[top[x]], id[x], val); x = par[top[x]]; } if (dep[x] > dep[y]) swap(x, y); update_addtag(1, 1, n, id[x], id[y], val); } void update_subtree(int x, int val) { if (lca(x, root) != x) update_addtag(1, 1, n, id[x], id[x] + size[x] - 1, val); else if (x == root) update_addtag(1, 1, n, 1, n, val); else { int s = findson(x, root); update_addtag(1, 1, n, 1, id[s] - 1, val); if (id[s] + size[s] <= n) update_addtag(1, 1, n, id[s] + size[s], n, val); } } int query_path(int x, int y) { int ans = 0; while (top[x] != top[y]) { if (dep[top[x]] < dep[top[y]]) swap(x, y); ans += query(1, 1, n, id[top[x]], id[x]); x = par[top[x]]; } if (dep[x] > dep[y]) swap(x, y); ans += query(1, 1, n, id[x], id[y]); return ans; } int query_subtree(int x) { int ans = 0; if (lca(x, root) != x) ans += query(1, 1, n, id[x], id[x] + size[x] - 1); else if (x == root) ans += query(1, 1, n, 1, n); else { int s = findson(x, root); ans += query(1, 1, n, 1, id[s] - 1); if (id[s] + size[s] <= n) ans += query(1, 1, n, id[s] + size[s], n); } return ans; } } // namespace solve_all using namespace solve_all; signed main() { scanf("%lld", &n); for (int i = 1; i <= n; i++) scanf("%lld", &b[i]); for (int i = 2; i <= n; i++) { int x; scanf("%lld", &x); add(x, i), add(i, x); } dfs1(1); dfs2(1, 1); build(1, 1, n); scanf("%lld", &q); while (q--) { int opt, x, y, z; scanf("%lld", &opt); if (opt == 1) scanf("%lld", &root); if (opt == 2) scanf("%lld%lld%lld", &x, &y, &z), update_path(x, y, z); if (opt == 3) scanf("%lld%lld", &x, &y), update_subtree(x, y); if (opt == 4) scanf("%lld%lld", &x, &y), printf("%lld\n", query_path(x, y)); if (opt == 5) scanf("%lld", &x), printf("%lld\n", query_subtree(x)); } return 0; }
还有用树状数组的:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 100005 int n, m, root = 1, a[N]; int head[N]; struct edgeType { int to, next; } edge[N << 1]; inline void addEdge(int from, int to) { static int cnt = 0; edge[++cnt] = (edgeType){ to, head[from] }; head[from] = cnt; } class BinaryIndexedTree { private: long long c[2][N]; inline int lowbit(int x) { return x & (-x); } inline void update(int x, long long v) { for (int i = x; i <= n; i += lowbit(i)) c[0][i] += v, c[1][i] += x * v; } inline long long query(int x) { long long ans = 0; for (int i = x; i > 0; i -= lowbit(i)) ans += (x + 1) * c[0][i] - c[1][i]; return ans; } public: inline void update(int l, int r, long long v) { update(l, v); update(r + 1, -v); } inline long long query(int l, int r) { return query(r) - query(l - 1); } } BIT; class TreeChain { private: int root, parent[N], size[N], depth[N], son[N], top[N], tid[N], ted[N]; inline void dfsPre(int u, int p, int d) { parent[u] = p; depth[u] = d; size[u] = 1; for (int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; if (v == p) continue; dfsPre(v, u, d + 1); size[u] += size[v]; if (size[v] > size[son[u]]) son[u] = v; } } inline void dfsTop(int u, int tp) { static int dfsClock = 0; top[u] = tp; tid[u] = ++dfsClock; if (son[u]) dfsTop(son[u], tp); for (int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; if (v == parent[u] || v == son[u]) continue; dfsTop(v, v); } ted[u] = dfsClock; } inline int reach(int x, int y) { int ans = 0; while (top[x] != top[y]) { ans = top[x]; x = parent[top[x]]; } return x == y ? ans : son[y]; } public: inline void init(int rt) { root = rt; dfsPre(root, 0, 1); dfsTop(root, root); } inline void changeRoot(int rt) { root = rt; } inline void updateInit(int x, int v) { BIT.update(tid[x], tid[x], v); } inline void updateRoute(int x, int y, int z) { while (top[x] != top[y]) { if (depth[top[x]] < depth[top[y]]) std::swap(x, y); BIT.update(tid[top[x]], tid[x], z); x = parent[top[x]]; } if (depth[x] > depth[y]) std::swap(x, y); BIT.update(tid[x], tid[y], z); } inline long long queryRoute(int x, int y) { long long ans = 0; while (top[x] != top[y]) { if (depth[top[x]] < depth[top[y]]) std::swap(x, y); ans += BIT.query(tid[top[x]], tid[x]); x = parent[top[x]]; } if (depth[x] > depth[y]) std::swap(x, y); return ans + BIT.query(tid[x], tid[y]); } inline void updateSubtree(int x, int z) { if (x == root) BIT.update(1, n, z); else if (tid[x] <= tid[root] && ted[x] >= ted[root]) { int t = reach(root, x); BIT.update(1, tid[t] - 1, z); BIT.update(ted[t] + 1, n, z); } else BIT.update(tid[x], ted[x], z); } inline long long querySubtree(int x) { if (x == root) return BIT.query(1, n); else if (tid[x] <= tid[root] && ted[x] >= ted[root]) { int t = reach(root, x); return BIT.query(1, tid[t] - 1) + BIT.query(ted[t] + 1, n); } else return BIT.query(tid[x], ted[x]); } }; TreeChain TC; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 2; i <= n; i++) { int x; scanf("%d", &x); addEdge(i, x); addEdge(x, i); } TC.init(1); for (int i = 1; i <= n; i++) TC.updateInit(i, a[i]); scanf("%d", &m); for (int i = 1; i <= m; i++) { int opt, x, y, z; scanf("%d%d", &opt, &x); if (opt == 1) TC.changeRoot(x); else if (opt == 2) { scanf("%d%d", &y, &z); TC.updateRoute(x, y, z); } else if (opt == 3) { scanf("%d", &y); TC.updateSubtree(x, y); } else if (opt == 4) { scanf("%d", &y); printf("%lld\n", TC.queryRoute(x, y)); } else { printf("%lld\n", TC.querySubtree(x)); } } return 0; }