[LNOI2014]LCA 解题报告

对于一棵 \(n\) 个节点的树,给出 \(m\) 次询问,每次给出 \(l,r,x\) ,求 \(\sum\limits_{i=l}^r depth(LCA(i,x))\)

\(n,m\le 5\times 10^4 , 1\le l\le r\le n , x\le n\)

一道不错的题目。

说明有时候用一些其他的做法求一个简单的东西也可以帮助思考。

对于我,求LCA都是用倍增来做的,但这题要求区间对于一个节点x的LCA的深度之和,如果只是在想用倍增的手法是做不出来的。

由于只是深度,并不需要得出切实的LCA,这就给我们提供了一个很好的方向。

考虑另外一种求LCA深度的做法。

如果要求x和y的LCA的深度,可以先将x到根的路径全部赋值为1,再在统计y到根的路径就可以得到LCA的深度了。

这个做法有一个很好的性质,那就是可加性。

所以只要把区间里的所有节点到根的路径都加1,最后在统计节点x到根的路径的值就行了。

首先链上的修改和求和可以用树链剖分做到 \(\mathcal{O(\log n)}\) ,考虑如何把区间给弄掉。

由于可加性和可减性,通过差分就可以将区间变为前缀相减,然后将查询拆成两个,离线排序一下,从1号节点不断修改到n号节点,如果遇到了对应的前缀查询,直接查询存储即可。

总效率 \(\mathcal{O(n\log n+m\log n)}\) ,精细实现可以做到 \(\mathcal{O(n\log n)}\)

然而实际上这个将LCA深度转换为链上求和的方法也是一种差分思想,所以就出现了加强版的旧词

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;

const int M=5e4+5,JYY=201314;

void swap(int &x,int &y){ x^=y^=x^=y; }
int min(int x,int y){ return x<y?x:y; }
int max(int x,int y){ return x>y?x:y; }

int n,q,fa[M],de[M],ans[M],pre[M];
struct Ques{
	int x,z,id,zf;
}Q[M<<1];

int read(){
	int x=0,y=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') y=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*y;
}

int tot=0,first[M];
struct Edge{
	int nxt,to;
}e[M<<1];
void add(int x,int y){
	e[++tot].nxt=first[x];
	first[x]=tot;
	e[tot].to=y;
}

struct Tree{
	int sum,len,lazy;
}tr[M<<2];
void pushup(int u){
	tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

void pushdown(int u){
	if(!tr[u].lazy) return ;
	tr[u<<1].sum+=tr[u<<1].len*tr[u].lazy;
	tr[u<<1].lazy+=tr[u].lazy;
	tr[u<<1|1].sum+=tr[u<<1|1].len*tr[u].lazy;
	tr[u<<1|1].lazy+=tr[u].lazy;
	tr[u].lazy=0;
}

void build(int u,int l,int r){
	tr[u].sum=tr[u].lazy=0;
	tr[u].len=r-l+1;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(u<<1,l,mid);
	build(u<<1|1,mid+1,r);
	pushup(u);
	return ;
}

void change(int u,int l,int r,int L,int R,int x){
	if(l>R||r<L) return ;
	if(l>=L&&r<=R){
		tr[u].sum+=tr[u].len*x;
		tr[u].lazy+=x;
		return ;
	}
	pushdown(u);
	int mid=(l+r)>>1;
	change(u<<1,l,mid,L,R,x);
	change(u<<1|1,mid+1,r,L,R,x);
	pushup(u);
	return ;
}

int query(int u,int l,int r,int L,int R){
	if(l>R||r<L) return 0;
	if(l>=L&&r<=R) return tr[u].sum;
	pushdown(u);
	int mid=(l+r)>>1;
	return query(u<<1,l,mid,L,R)+query(u<<1|1,mid+1,r,L,R);
}

int num=0,St[M],En[M],son[M],top[M],dfn[M],size[M];
void dfs1(int u,int fa){
	de[u]=de[fa]+1;
	size[u]=1;
	for(int i=first[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue ;
		dfs1(v,u);
		size[u]+=size[v];
		if(size[son[u]]<size[v]){
			son[u]=v;
		}
	}
}

void dfs2(int u,int fa,int tp){
	St[u]=++num;
	top[u]=tp;
	if(son[u]) dfs2(son[u],u,tp);
	for(int i=first[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa||v==son[u]) continue ;
		dfs2(v,u,v);
	}
	En[u]=num;
}

void Change(int x,int d){
	while(top[x]!=1){
		change(1,1,n,St[top[x]],St[x],d);
		x=fa[top[x]];
	}
	change(1,1,n,St[1],St[x],d);
	return ;
}

int Query(int x){
	int res=0;
	while(top[x]!=1){
		res=(res+query(1,1,n,St[top[x]],St[x]))%JYY;
		x=fa[top[x]];
	}
	res=(res+query(1,1,n,St[1],St[x]))%JYY;
	return res;
}

bool cmp(Ques x,Ques y){
	return x.x<y.x;
}

int main(){
	n=read(),q=read();
	for(int i=2;i<=n;i++){
		fa[i]=read()+1;
		add(fa[i],i);
	}
	dfs1(1,0);
	dfs2(1,0,1);
	build(1,1,n);
	for(int i=1;i<=n;i++){
		pre[i]=(pre[i-1]+de[i])%JYY;
	}
	for(int i=1;i<=q;i++){
		int l=read()+1,r=read()+1,z=read()+1;
		Q[i*2-1].x=l-1;
		Q[i*2-1].z=z;
		Q[i*2-1].zf=-1;
		Q[i*2-1].id=i;
		Q[i*2].x=r;
		Q[i*2].z=z;
		Q[i*2].zf=1;
		Q[i*2].id=i;
	}
	sort(Q+1,Q+q*2+1,cmp);
	int now=0;
	for(int i=1;i<=2*q;i++){
		while(now<Q[i].x){
			now++;
			Change(now,1);
		}
		ans[Q[i].id]=(ans[Q[i].id]+Query(Q[i].z)*Q[i].zf+JYY)%JYY;
	}
	for(int i=1;i<=q;i++){
		printf("%d\n",ans[i]);
	}
}
posted @ 2020-08-01 22:07  Dabuliuzp  阅读(143)  评论(0编辑  收藏  举报
/* */ 返回顶端