【模板】轻重链剖分 treecut
posted on 2021-11-16 12:50:02 | under 模板 | source
英语不好啊,就叫 treecut 吧,不管了
treecut 速成(确信):
- 重儿子:所有儿子节点中 siz 最大的。重链:以轻儿子(钦定 root 为轻儿子)开始一直往重儿子走的一条链。
- dfs 出 dep,fa,siz,son(重儿子)
- cut 求 dfn,rnk,top,分别是 dfs 序,满足 rnk[dfn[u]]=u 的 rnk,这个节点所在的重链的顶端。优先 cut 重儿子
- 路径,像倍增 lca 一样跳,跳到重链顶,由于一条重链的 dfn 连续,所以可以用线段树维护
- 子树,一个点的儿子和后面的孙子的 dfn 连续,也可以线段树
- 支持操作:修改/询问 路径/子树 上的 线段树能支持的操作,当然还有 lca
注意,往上跳时选 dep[top[]] 更深的!
草了怎么树剖还能换根,补一下:
题目加入换根操作(例:LOJ 树剖模板),这时不用重新剖这棵树,分讨:
- 对于 \((u,v)\) 这个路径,换根后对答案无影响
- 对于 \(u\) 的子树,记最近一次换根换成了 \(root\):
- 如果 \(u=root\),全部点都对答案有贡献。
- 如果 \(root\) 在 \(u\) 子树中,对答案有贡献的点是 除 \(root\) 所在子树 的所有点,原因:子树实际上是全部减去 \(u\) 上面那一堆,现在根在子树里,想象把 \(root\) 用手提起来,\(root\) 所在子树就在 \(u\) 头上了,直接弃之。实现时找 \(root\) 所在子树时可以默写 LA 模板并套用。
- 否则,正常操作。
- 对于 \(\operatorname{lca}(u,v)\):
- 原树上 \((u,v),(u,r),(v,r)\) 的 LCA 深度最大的那一个,想一下为什么?树上每一个点只有一个父亲哦。
- 对于 \(u\) 的 \(k\) 级祖先:
- 考虑 \(u\to r\) 这一条路径,原树 LCA 为 \(k\)。
- 如果在 \(k\) 下面,直接跳;否则从 \(r\) 开始反向跳。
Luogu 板子(没取模)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<int N,int M,class T=int> struct graph{
int cnt,head[N+10],nxt[M*2+10];
struct edge{
int u,v;T w;
edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
} e[M*2+10];
edge operator[](int i){return e[i];}
graph():cnt(0){memset(head,0,sizeof head);}
void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N> struct segtree{
LL tag[N*4+10],ans[N*4+10];
#define mid ((l+r)>>1)
segtree(){memset(tag,0,sizeof tag),memset(ans,0,sizeof ans);}
void add(LL k,int l,int r,int p){tag[p]+=k,ans[p]+=k*(r-l+1);}
void pushup(int p){ans[p]=ans[p<<1]+ans[p<<1|1];}
void pushdown(int l,int r,int p){add(tag[p],l,mid,p<<1),add(tag[p],mid+1,r,p<<1|1),tag[p]=0;}
void build(LL (*w)(int),int l=1,int r=N,int p=1){
if(l==r) return ans[p]=w(l),void();
build(w,l,mid,p<<1),build(w,mid+1,r,p<<1|1),pushup(p);
}
void modify(LL k,int L,int R,int l=1,int r=N,int p=1){
if(r<L||R<l) return ;
if(L<=l&&r<=R) return add(k,l,r,p);
pushdown(l,r,p),modify(k,L,R,l,mid,p<<1),modify(k,L,R,mid+1,r,p<<1|1),pushup(p);
}
LL query(int L,int R,int l=1,int r=N,int p=1){
if(r<L||R<l) return 0;
if(L<=l&&r<=R) return ans[p];
return pushdown(l,r,p),query(L,R,l,mid,p<<1)+query(L,R,mid+1,r,p<<1|1);
}
#undef mid
};
int n,m,root;
LL a[100010];
graph<100010,100010> g;
segtree<100010> t;
int fa[100010],dep[100010],siz[100010],son[100010];
int dfn[100010],rnk[100010],top[100010],cnt;
LL getw(int u){return a[rnk[u]];}
int dfs(int u,int f=0){
dep[u]=dep[fa[u]=f]+(siz[u]=1);
for(int i=g.head[u];i;i=g.nxt[i]){int v=g[i].v;
if(v==f) continue;
siz[u]+=dfs(v,u);
if(siz[son[u]]<siz[v]) son[u]=v;
}
return siz[u];
}
void cut(int u,int topf){
top[rnk[dfn[u]=++cnt]=u]=topf;
if(son[u]) cut(son[u],topf);
for(int i=g.head[u];i;i=g.nxt[i]){int v=g[i].v;
if(v==fa[u]||v==son[u]) continue;
cut(v,v);
}
}
LL query(int u,int v){
LL res=0;
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res+=t.query(dfn[top[u]],dfn[u]);
}
if(dep[u]<dep[v]) swap(u,v);
return res+=t.query(dfn[v],dfn[u]);
}
void modify(LL k,int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
t.modify(k,dfn[top[u]],dfn[u]);
}
if(dep[u]<dep[v]) swap(u,v);
t.modify(k,dfn[v],dfn[u]);
}
int main(){
scanf("%d%d%d",&n,&m,&root);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<n;i++){int u,v;
scanf("%d%d",&u,&v);
g.link(u,v);
}
dfs(root),cut(root,root),t.build(getw);
for(int i=1;i<=m;i++){int op,u,v;LL k;
scanf("%d",&op);
if(op==1) scanf("%d%d%lld",&u,&v,&k),modify(k,u,v);
else if(op==2) scanf("%d%d",&u,&v),printf("%lld\n",query(u,v));
else if(op==3) scanf("%d%lld",&u,&k),t.modify(k,dfn[u],dfn[u]+siz[u]-1);
else if(op==4) scanf("%d",&u),printf("%lld\n",t.query(dfn[u],dfn[u]+siz[u]-1));
}
return 0;
}
和线段树绑在一起的板子
template<int N,class T,class A,class W=bool> struct treecut{
int fa[N+10],dep[N+10],siz[N+10],son[N+10];
int cnt,top[N+10],dfn[N+10],rnk[N+10];
graph<N,N,W> &g;segtree<N,T,A> &t;
treecut(graph<N,N,W> &g,segtree<N,T,A> &t):cnt(0),g(g),t(t){memset(son,siz[0]=0,sizeof son);}
void dfs(int u,int f=0){
dep[u]=dep[fa[u]=f]+1;
siz[u]=1;
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v;
if(v==fa[u]) continue;
dfs(v,u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void cut(int u,int topf){
top[rnk[dfn[u]=++cnt]=u]=topf;
if(son[u]) cut(son[u],topf);
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v;
if(v==fa[u]||v==son[u]) continue;
cut(v,v);
}
}
void modify_p(T k,int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
t.modify(k,dfn[top[u]],dfn[u],t.root);
}
if(dep[u]>dep[v]) swap(u,v);
t.modify(k,dfn[u],dfn[v],t.root);
}
A query_p(int u,int v){
A res=A();
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=res+t.query(dfn[top[u]],dfn[u],t.root);
}
if(dep[u]>dep[v]) swap(u,v);
return res=res+t.query(dfn[u],dfn[v],t.root);
}
void modify_st(T k,int u){t.modify(k,dfn[u],dfn[u]+siz[u]-1,t.root);}
A query_st(int u){return t.query(dfn[u],dfn[u]+siz[u]-1,t.root);}
};
通用的树剖其实只需要 LCA 和 LA
template<int N,int M,class T=int> struct treecut: public graph<N,M,T>{
graph<N,M,T> &g=*this;
int fa[N+10],dep[N+10],son[N+10],siz[N+10],
dfn[N+10],rnk[N+10],top[N+10],cnt;
treecut(){memset(son,cnt=siz[0]=0,sizeof son);}
void dfs(int u,int f=0){
dep[u]=dep[fa[u]=f]+1,siz[u]=1;
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa[u]) continue;
dfs(v,u),siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void cut(int u,int topf){
top[rnk[dfn[u]=++cnt]=u]=topf;
if(son[u]) cut(son[u],topf);
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa[u]||v==son[u]) continue;
cut(v,v);
}
}
int lca(int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]) if(dep[top[u]]<dep[top[v]]) swap(u,v);
if(dep[u]<dep[v]) swap(u,v); return v;
}
int jump(int u,int k){
for(;dfn[u]-dfn[top[u]]+1<=k;k-=dfn[u]-dfn[top[u]]+1,u=fa[top[u]]);
return rnk[dfn[u]-k];
}
};
另一种写法
int lca(int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]) if(dep[top[u]]<dep[top[v]]) swap(u,v);
return dep[u]<dep[v]?u:v;
}
int jump(int u,int k){
if(!u) return 0; int len=dfn[u]-dfn[top[u]]+1;
return len<=k?jump(fa[top[u]],k-len):rnk[dfn[u]-k];
}
注意不能 dfn[u]-dfn[fa[top[u]]]
LOJ 板子(换根)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
template<int N,int M,class T=bool> struct graph{
int head[N+10],nxt[M*2+10],cnt;
struct edge{
int u,v;T w;
edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
} e[M*2+10];
graph(){memset(head,cnt=0,sizeof head);}
edge operator[](int i){return e[i];}
void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N,class T,class A> struct segtree{
T tag[N*20+10];A ans[N*20+10];
int ch[N*20+10][2],cnt,root;
segtree():cnt(-1){root=0;newnode();}
void add(T k,int &p,int l,int r){if(!p) p=newnode();ans[p].add(k,l,r),tag[p]+=k;}
int newnode(){return tag[++cnt]=T(),ans[cnt]=A(),ch[cnt][0]=ch[cnt][1]=0,cnt;}
void maintain(int &p){ans[p]=ans[ch[p][0]]+ans[ch[p][1]];}
void pushdown(int &p,int l,int r){
if(tag[p].empty()) return ;
int mid=(l+r)>>1;
add(tag[p],ch[p][0],l,mid);
add(tag[p],ch[p][1],mid+1,r);
tag[p]=T();
}
void modify(T k,int L,int R,int &p,int l=1,int r=N){
if(!p) p=newnode();
if(L<=l&&r<=R) return add(k,p,l,r);
int mid=(l+r)>>1;
pushdown(p,l,r);
if(L<=mid) modify(k,L,R,ch[p][0],l,mid);
if(mid+1<=R) modify(k,L,R,ch[p][1],mid+1,r);
maintain(p);
}
A query(int L,int R,int &p,int l=1,int r=N){
if(!p) return A();
if(L<=l&&r<=R) return ans[p];
int mid=(l+r)>>1;A res=A();
pushdown(p,l,r);
if(L<=mid) res=res+query(L,R,ch[p][0],l,mid);
if(mid+1<=R) res=res+query(L,R,ch[p][1],mid+1,r);
return res;
}
};
template<int N,class T,class A,class W=bool> struct treecut{
typedef graph<N,N,W> Graph;
typedef segtree<N,T,A> Segtree;
int fa[N+10],dep[N+10],siz[N+10],son[N+10],
dfn[N+10],rnk[N+10],top[N+10],cnt,root,root_o;
Graph &g;Segtree &t;
treecut(Graph &g,Segtree &t):cnt(0),root(1),g(g),t(t){memset(son,siz[0]=0,sizeof son);}
int dfs(int u,int f=0){
dep[u]=dep[fa[u]=f]+1,siz[u]=1;
for(int i=g.head[u];i;i=g.nxt[i]){int v=g[i].v;
if(v==fa[u]) continue;
siz[u]+=dfs(v,u);
if(siz[son[u]]<siz[v]) son[u]=v;
}
return siz[u];
}
void cut(int u,int topf){
top[rnk[dfn[u]=++cnt]=u]=topf;
if(son[u]) cut(son[u],topf);
for(int i=g.head[u];i;i=g.nxt[i]){int v=g[i].v;
if(v==fa[u]||v==son[u]) continue;
cut(v,v);
}
}
void build(){dfs(root_o=root),cut(root,root);}
int jump(int u,int k){//对于原树
for(;k>=dfn[u]-dfn[top[u]]+1&&u!=root_o;u=fa[top[u]]){
k-=dfn[u]-dfn[top[u]]+1;
}
return dfn[u]>=k?rnk[dfn[u]-k]:0;
}
int lca(int u,int v){//对于原树
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
}
if(dep[u]>dep[v]) swap(u,v);
return u;
}
void modify_p(T k,int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
t.modify(k,dfn[top[u]],dfn[u],t.root);
}
if(dep[u]>dep[v]) swap(u,v);
t.modify(k,dfn[u],dfn[v],t.root);
}
A query_p(int u,int v){
A res=A();
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=res+t.query(dfn[top[u]],dfn[u],t.root);
}
if(dep[u]>dep[v]) swap(u,v);
return res+t.query(dfn[u],dfn[v],t.root);
}
void modify_st(T k,int u){
if(u==root) return t.modify(k,1,cnt,t.root);//all
if(dfn[u]<=dfn[root]&&dfn[root]<=dfn[u]+siz[u]-1){
int s=jump(root,dep[root]-dep[u]-1);
int l=dfn[s],r=dfn[s]+siz[s]-1;
if(l!=1) t.modify(k,1,l-1,t.root);
if(r!=cnt) t.modify(k,r+1,cnt,t.root);
}else{
t.modify(k,dfn[u],dfn[u]+siz[u]-1,t.root);
}
}
A query_st(int u){
if(u==root) return t.query(1,cnt,t.root);
if(dfn[u]<=dfn[root]&&dfn[root]<=dfn[u]+siz[u]-1){
int s=jump(root,dep[root]-dep[u]-1);
int l=dfn[s],r=dfn[s]+siz[s]-1;
A res=A();
if(l!=1) res=res+t.query(1,l-1,t.root);
if(r!=cnt) res=res+t.query(r+1,cnt,t.root);
return res;
}else{
return t.query(dfn[u],dfn[u]+siz[u]-1,t.root);
}
}
};
struct Tag{
LL x;
Tag(LL x=0):x(x){}
bool empty(){return x==0;}
Tag operator+=(Tag b){return x+=b.x,*this;}
};
struct Ans{
LL x;
Ans(LL x=0):x(x){}
friend Ans operator+(Ans a,Ans b){return Ans(a.x+b.x);}
Ans add(Tag b,int l,int r){return x+=b.x*(r-l+1),*this;}
};
int n,m,a[100010];
graph<100010,100010> g;
segtree<100010,Tag,Ans> t;
treecut<100010,Tag,Ans> tc(g,t);
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=2;i<=n;i++){int fa;scanf("%d",&fa),g.link(i,fa);}
tc.build();
for(int i=1;i<=n;i++) tc.modify_p(Tag(a[i]),i,i);
scanf("%d",&m);
for(int i=1;i<=m;i++){char op;int u,v,k;
scanf(" %c%d",&op,&u);
if(op=='1') tc.root=u;
else if(op=='2') scanf("%d%d",&v,&k),tc.modify_p(Tag(k),u,v);
else if(op=='3') scanf("%d",&k),tc.modify_st(Tag(k),u);
else if(op=='4') scanf("%d",&v),printf("%lld\n",tc.query_p(u,v).x);
else if(op=='5') printf("%lld\n",tc.query_st(u).x);
}
return 0;
}
CF916E(换根 LCA 和子树)
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#include <functional>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int lowbit(int x){return x&-x;}
template<int N,int M,class T=int> struct graph{
int cnt,head[N+10],nxt[M*2+10];
struct edge{
int u,v;T w;
edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
} e[M*2+10];
edge operator[](int i){return e[i];}
graph():cnt(0){memset(head,0,sizeof head);}
void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
template<int N,class T=int> struct fenwick{
T t[N+10];
fenwick(){memset(t,0,sizeof t);}
void add(T k,int p){for(;p<=N;p+=p&-p) t[p]+=k;}
T query(int p){T r=0;for(;p>=1;p-=p&-p) r+=t[p];return r;}
};
template<int N,class T=int> struct segtree{
fenwick<N,T> s,t;
void add(T k,int p){s.add(k,p),t.add(k*(p-1),p);}
void add(T k,int l,int r){add(k,l),add(-k,r+1);}
T query(int p){return s.query(p)*p-t.query(p);}
T query(int l,int r){return query(r)-query(l-1);}
};
template<int N,int M,class T=int> struct treecut: public graph<N,M,T>{
graph<N,M,T> &g=*this;
int fa[N+10],dep[N+10],son[N+10],siz[N+10],
dfn[N+10],rnk[N+10],top[N+10],cnt;
treecut(){memset(son,cnt=siz[0]=0,sizeof son);}
void dfs(int u,int f=0){
dep[u]=dep[fa[u]=f]+1,siz[u]=1;
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa[u]) continue;
dfs(v,u),siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
void cut(int u,int topf){
top[rnk[dfn[u]=++cnt]=u]=topf;
if(son[u]) cut(son[u],topf);
for(int i=g.head[u];i;i=g.nxt[i]){
int v=g[i].v; if(v==fa[u]||v==son[u]) continue;
cut(v,v);
}
}
int lca(int u,int v){
for(;top[u]!=top[v];u=fa[top[u]]) if(dep[top[u]]<dep[top[v]]) swap(u,v);
if(dep[u]<dep[v]) swap(u,v); return v;
}
int jump(int u,int k){
for(;dfn[u]-dfn[top[u]]+1<=k;k-=dfn[u]-dfn[top[u]]+1,u=fa[top[u]]);
return rnk[dfn[u]-k];
}
};
int r,n,m,a[100010];
treecut<100010,100010> g;
segtree<100010,LL> t;
int getlca(int u,int v){
return max({g.lca(u,v),g.lca(u,r),g.lca(v,r)},[&](int u,int v){return g.dep[u]<g.dep[v];});
}
void perform(int u,function<void(int,int)> f){
if(u==r) return f(1,n);
if(g.dfn[u]<=g.dfn[r]&&g.dfn[r]<g.dfn[u]+g.siz[u]){
int v=g.jump(r,g.dep[r]-g.dep[u]-1);
if(1<g.dfn[v]) f(1,g.dfn[v]-1);
if(g.dfn[v]+g.siz[v]<=n) f(g.dfn[v]+g.siz[v],n);
}else f(g.dfn[u],g.dfn[u]+g.siz[u]-1);
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.link(u,v);
g.dfs(1),g.cut(1,1);
for(int i=1;i<=n;i++) t.add(a[i],g.dfn[i],g.dfn[i]);
for(int i=1,op,u,v,x;i<=m;i++){
scanf("%d%d",&op,&u);
if(op==1) r=u;
else if(op==2) scanf("%d%d",&v,&x),perform(getlca(u,v),[&](int l,int r){t.add(x,l,r);});
else{LL ans=0;perform(u,[&](int l,int r){ans+=t.query(l,r);}),printf("%lld\n",ans);}
}
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/template-treecut.html