题解 LGP4211【[LNOI2014]LCA】/【模板】全局平衡二叉树

problem

一棵树,多次给定 \(l,r,z\) 询问 \(\sum_{l\leq i\leq r}dep_{lca(i,z)}\),允许离线,\(n\leq 50000\)

solution

转换:这个 \(dep_u\) 的定义为 \(u\) 到根节点的点数。如果我们对于每个 \(l\leq i\leq r\),都给 \(1\leftrightarrow i\) 这条链加一,那么直接询问 \(1\leftrightarrow z\) 上的点权之和就可以了。

考虑到 \([l,r]\) 的答案可以差分成 \([1,l-1]\)\([1,r]\) 的答案,直接扫描线即可。

现在的问题变成了树链加,树链求和。

全局平衡二叉树

全局平衡二叉树可以解决链修改、链查询的问题,复杂度为单次 \(O(\log n)\)。运用了 LCT 的思想。

以下是全局平衡二叉树的构建。对于一棵树,我们首先给它做重链剖分。将每条重链拎出来单独建一棵二叉树,这棵二叉树的每个点的权值是它的轻子树大小和加上它自己(\(siz_u-siz_{son_u}\)),然后每一层都取带权中心;这个二叉树的中序遍历恰是深度从浅到深的重链遍历。重链头的父亲是它在原树上的父亲,且这个父亲不记录这个儿子。

接下的任务是修改和查询。我们可以先考虑一个点到根的路径怎么表示。

第一步是从这个点暴力跳全局平衡二叉树的父亲。观察到原树上跳轻边,因为重链剖分,所以只有 \(O(\log n)\) 条轻边;除了轻边外就是二叉树里的边,二叉树的边,每跳一次总的轻子树大小就翻倍(考虑总权重,带权中心),所以一共也就 \(O(\log n)\) 条边。所以这一步暴力跳父亲一共 \(O(\log n)\) 次。

第二步,有懒标记的从上向下下传。标记永久化的不管。

第三步,从下向上,对于询问点和从轻边跳上去的点(统称 \(p\)),我们要加比它们深度小的所有点。那么我们从 \(p\) 点逐个父亲跳上去的时候,如果当前点的右儿子是上一个点,说明当前点和它左子树比 \(p\) 在原树的深度都小,都要加,在当前点这个地方加一次,在它的左子树打子树加的懒标记。(或者标记永久化也行,这样这个标记贡献要继续上传,跳出轻边时记得删掉)这样就能处理完所有的。

我们相当于把询问的链拆成若干点和它们的左子树,而这个子树加天然带有懒标记/标记永久化的结构,这样就省去一个线段树。(实际上可以认为这个左子树是不动的 splay 树,整个是一个 splay 森林——所以它本质上是不动的 LCT)

原树上任意两点的链,更加简单。首先考虑任意两点 LCA 怎么做。暴跳全局平衡二叉树的父亲,叠起来即可。\(O(\log n)\)。和暴力一样。在原树上任意两点的链的修改和询问,那和刚才到根的完全一致,除了 LCA 处的左子树不用加。

全局平衡二叉树可以优化动态 DP。子树操作可以维护轻子树信息,不确定能不能做。

Codes

全局平衡二叉树
void dfs(int u){
	siz[u]=1;
	for(int v:g[u]) if(v!=fa[u]){
		dfs(v),siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
}
int cf[1<<16],ch[1<<16][2],cs[1<<16];//LCT father
int chain[1<<16];
int cbuild(int l,int r){
	if(l==r) return cs[chain[l]]=1,chain[l];
	int s=0,now=0,mid;
	for(int i=l;i<=r;i++) s+=siz[chain[i]]-siz[son[chain[i]]];
	for(int i=l;i<=r;i++){
		now+=siz[chain[i]]-siz[son[chain[i]]];
		if(now<<1>=s){mid=i;break;}
	}
	int x=chain[mid]; cs[x]=r-l+1;
	if(l<mid) cf[ch[x][0]=cbuild(l,mid-1)]=x;
	if(mid<r) cf[ch[x][1]=cbuild(mid+1,r)]=x;
	return x;
}
int build(int u){
	for(int x=u;x;x=son[x]) for(int v:g[x]) if(v!=fa[x]&&v!=son[x]) cf[build(v)]=x;
	int cnt=0;
	for(int x=u;x;x=son[x]) chain[++cnt]=x;
	return cbuild(1,cnt);
}
LL ans[1<<16],tag[1<<16],his[1<<16];
void maintain(int p){if(p) ans[p]=ans[ch[p][0]]+ans[ch[p][1]]+his[p];}
void spread(int p,int k){if(p) ans[p]+=cs[p]*k,tag[p]+=k,his[p]+=k;}
void pushdown(int p){if(p&&tag[p]) spread(ch[p][0],tag[p]),spread(ch[p][1],tag[p]),tag[p]=0;}
void modify(int p){
	int cnt=0;
	for(int u=p;u;u=cf[u]) chain[++cnt]=u;
	for(int i=cnt;i>=1;i--) pushdown(chain[i]);
	for(int i=1;i<=cnt;i++){
		int u=chain[i];
		if(i==1||ch[u][0]!=chain[i-1]){
			his[u]++;
			if(ch[u][0]) spread(ch[u][0],1);
		}
		maintain(u);
	}
}
LL query(int p){
	int cnt=0;
	for(int u=p;u;u=cf[u]) chain[++cnt]=u;
	for(int i=cnt;i>=1;i--) pushdown(chain[i]);
	LL res=0;
	for(int i=1;i<=cnt;i++){
		int u=chain[i];
		if(i==1||ch[u][0]!=chain[i-1]){
			res+=his[u];
			if(ch[u][0]) res+=ans[ch[u][0]];
		}
	}
	return res;
}
树剖线段树
#include <cstdio>
#include <vector>
#include <cstring>
#include <cassert>
#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;
const int P=201314;
void red(LL&x){x=(x%P+P)%P;}
template<class T> struct Ans{
	int len; T sum;
	Ans(int len=1,T sum=0):len(len),sum(sum%P){}
	Ans operator+(Ans b){return Ans(len+b.len,sum+b.sum);}
	Ans operator+=(int k){return red(sum+=1ll*k*len),*this;}
};
template<int N,class T,class A> struct segtree{
	T tag[N<<2]; A ans[N<<2];
	segtree(){build();}
	void build(int p=1,int l=1,int r=N){
		if(l==r) return ; int mid=(l+r)>>1;
		build(p<<1,l,mid),build(p<<1|1,mid+1,r);
		ans[p]=ans[p<<1]+ans[p<<1|1];
	}
	void add(T k,int p){tag[p]+=k,ans[p]+=k;}
	void pushdown(int p){add(tag[p],p<<1),add(tag[p],p<<1|1),tag[p]=0;}
	void modify(int L,int R,int k,int p=1,int l=1,int r=N){
		if(L<=l&&r<=R) return add(k,p);
		int mid=(l+r)>>1; pushdown(p);
		if(L<=mid) modify(L,R,k,p<<1,l,mid);
		if(mid<R) modify(L,R,k,p<<1|1,mid+1,r);
		ans[p]=ans[p<<1]+ans[p<<1|1]; 
	}
	A query(int L,int R,int p=1,int l=1,int r=N){
		if(L<=l&&r<=R) return ans[p];
		int mid=(l+r)>>1; A res=A(0,0); pushdown(p);
		if(L<=mid) res=query(L,R,p<<1,l,mid);
		if(mid<R) res=res+query(L,R,p<<1|1,mid+1,r);
		return res;
	}
};
template<int N,int M,class T=int> 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];
	edge& operator[](int i){return e[i];}
	graph(){memset(head,cnt=0,sizeof head);}
	void add(int u,int v,int w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
	void link(int u,int v,int w=0){add(u,v,w),add(v,u,w);}
};
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],siz[N+10],son[N+10],dep[N+10],
		dfn[N+10],top[N+10],rnk[N+10],cnt;
	treecut():cnt(0){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[v]>siz[son[u]]) 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 split(int u,int v,function<void(int,int)> op){
		for(;top[u]!=top[v];u=fa[top[u]]){
			if(dep[top[u]]<dep[top[v]]) swap(u,v);
			op(dfn[top[u]],dfn[u]);
		}
		if(dep[u]<dep[v]) swap(u,v);
		op(dfn[v],dfn[u]);
	}
};
struct ask{
	int pos,w,id,u;
	ask(int pos=0,int w=0,int id=0,int u=0):pos(pos),w(w),id(id),u(u){}
	bool operator<(ask b){return pos<b.pos;}
};
int n,m,cnt;
LL ret[50010];
ask q[100010];
treecut<50010,50010> g;
segtree<50010,int,Ans<LL>> t;
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%d%*d",&n);
	for(int i=2,f;i<=n;i++) scanf("%d",&f),g.link(i,f+1);
	g.dfs(1),g.cut(1,1);
	for(int l,r,z;~scanf("%d%d%d",&l,&r,&z);){
		cnt++,z++,l++,r++;
		if(l>1) q[++m]=ask(l-1,-1,cnt,z);
		q[++m]=ask(r,1,cnt,z);
	}
	sort(q+1,q+m+1);
	for(int i=1,now=1;i<=n;i++){
		g.split(i,1,[&](int l,int r){
			assert(1<=l&&l<=r&&r<=n);
//			debug("modify(dfn)(%d,%d)\n",l,r);
			t.modify(l,r,1);
		});
		for(;now<=m&&q[now].pos==i;now++){
			g.split(q[now].u,1,[&](int l,int r){
				assert(1<=l&&l<=r&&r<=n);
//				debug("query(dfn)(%d,%d)\n",l,r);
				red(ret[q[now].id]+=t.query(l,r).sum*q[now].w);
			});
		}
	}
	for(int i=1;i<=cnt;i++) printf("%lld\n",ret[i]);
	return 0;
}

posted @ 2023-07-20 20:45  caijianhong  阅读(41)  评论(0编辑  收藏  举报