1. 程式人生 > >【BZOJ3626】[LNOI2014]LCA 離線+樹鏈剖分+線段樹

【BZOJ3626】[LNOI2014]LCA 離線+樹鏈剖分+線段樹

log 根路徑 iostream 根節點 2個 scrip har eof family

【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 離線+樹鏈剖分+線段樹