【模板】轻重链剖分 treecut

posted on 2021-11-16 12:50:02 | under 模板 | source

英语不好啊,就叫 treecut 吧,不管了

treecut 速成(确信):

  1. 重儿子:所有儿子节点中 siz 最大的。重链:以轻儿子(钦定 root 为轻儿子)开始一直往重儿子走的一条链。
  2. dfs 出 dep,fa,siz,son(重儿子)
  3. cut 求 dfn,rnk,top,分别是 dfs 序,满足 rnk[dfn[u]]=u 的 rnk,这个节点所在的重链的顶端。优先 cut 重儿子
  4. 路径,像倍增 lca 一样跳,跳到重链顶,由于一条重链的 dfn 连续,所以可以用线段树维护
  5. 子树,一个点的儿子和后面的孙子的 dfn 连续,也可以线段树
  6. 支持操作:修改/询问 路径/子树 上的 线段树能支持的操作,当然还有 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;
}
 
posted @ 2022-11-06 19:17  caijianhong  阅读(26)  评论(0编辑  收藏  举报