BZOJ3626[LNOI2014]LCA 离线+树链剖分+线段树

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3626[LNOI2014]LCA 离线+树链剖分+线段树相关的知识,希望对你有一定的参考价值。

【BZOJ3626】[LNOI2014]LCA

Description

给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

Input

第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。

Output

输出q行,每行表示一个询问的答案。每个答案对201314取模输出

Sample Input

5 2
0
0
1
1
1 4 3
1 4 2

Sample Output

8
5

HINT

共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。

题解:考虑dep[lca(a,b)]可以表示成什么,我们将a到根路径上的每个节点的权值都+1,然后查询b到根的路径上的所有点的权值和即可。

发现这个东西是可以用前缀和来搞的。我们将询问拆成前缀相减的形式,从1到n枚举i,将i到根路径上所有点的权值和+1,顺便处理当前的所有询问即可。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mod 201314
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=50010;
int n,m,sum,tot,cnt;
int to[maxn],next[maxn],head[maxn],deep[maxn],siz[maxn],fa[maxn],top[maxn],son[maxn],p[maxn];
int s[maxn<<2],t[maxn<<2],ans[maxn];
struct QUERY
{
	int qx,org,k,qz;
}q[maxn<<1];
inline int rd()
{
	int ret=0;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	gc=getchar();
	while(gc>=‘0‘&&gc<=‘9‘)	ret=(ret<<3)+(ret<<1)+gc-‘0‘,gc=getchar();
	return ret;
}
bool cmp(QUERY a,QUERY b)
{
	return a.qx<b.qx;
}
void dfs1(int x)
{
	siz[x]=1;
	for(int i=head[x];i!=-1;i=next[i])
	{
		deep[to[i]]=deep[x]+1;
		dfs1(to[i]);
		siz[x]+=siz[to[i]];
		if(siz[to[i]]>siz[son[x]])	son[x]=to[i];
	}
}
void dfs2(int x,int tp)
{
	top[x]=tp,p[x]=++tot;
	if(son[x])	dfs2(son[x],tp);
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=son[x])	dfs2(to[i],to[i]);
}
void pushdown(int l,int r,int x)
{
	if(t[x])
	{
		int mid=(l+r)>>1;
		s[lson]=(s[lson]+(mid-l+1)*t[x])%mod;
		s[rson]=(s[rson]+(r-mid)*t[x])%mod;
		t[lson]+=t[x],t[rson]+=t[x],t[x]=0;
	}
}
void updata(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)
	{
		s[x]+=r-l+1,t[x]++;
		return ;
	}
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,lson,a,b);
	if(b>mid)	updata(mid+1,r,rson,a,b);
	s[x]=(s[lson]+s[rson])%mod;
}
int query(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)	return s[x];
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(b<=mid)	return query(l,mid,lson,a,b);
	if(a>mid)	return query(mid+1,r,rson,a,b);
	return query(l,mid,lson,a,b)+query(mid+1,r,rson,a,b);
}
void insert(int x)
{
	while(x)	updata(1,n,1,p[top[x]],p[x]),x=fa[top[x]];
}
int getsum(int x)
{
	int ret=0;
	while(x)	ret=(ret+query(1,n,1,p[top[x]],p[x]))%mod,x=fa[top[x]];
	return ret;
}
void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	n=rd(),m=rd();
	int i,j;
	memset(head,-1,sizeof(head));
	for(i=2;i<=n;i++)	fa[i]=rd()+1,add(fa[i],i);
	deep[1]=1;
	dfs1(1),dfs2(1,1);
	for(i=1;i<=m;i++)
	{
		q[i*2-1].qx=rd(),q[i*2].qx=rd()+1;
		q[i*2-1].qz=q[i*2].qz=rd()+1;
		q[i*2-1].k=-1,q[i*2].k=1;
		q[i*2-1].org=q[i*2].org=i;
	}
	sort(q+1,q+2*m+1,cmp);
	for(i=1;i<=2*m;i++)
	{
		for(j=q[i-1].qx+1;j<=q[i].qx;j++)	insert(j);
		ans[q[i].org]+=q[i].k*getsum(q[i].qz);
	}
	for(i=1;i<=m;i++)	printf("%d\n",(ans[i]+mod)%mod);
	return 0;
}

 

以上是关于BZOJ3626[LNOI2014]LCA 离线+树链剖分+线段树的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ3626[LNOI2014]LCA 离线+树链剖分+线段树

BZOJ 3626: [LNOI2014]LCA 树链剖分 线段树 离线

BZOJ 3626 [LNOI2014]LCA:树剖 + 差分 + 离线将深度转化成点权之和

bzoj3626LNOI2014LCA

BZOJ3626: [LNOI2014]LCA

BZOJ 3626 [LNOI2014]LCA