BZOJ3626: [LNOI2014]LCA

题目大意:给一棵树,q个询问,每次询问一个区间内的点与一个点的所有LCA的深度之和


很神的一道题啊...

首先对于每组询问,我们可以把这个区间内每个点到根的路径都+1,然后求被询问的点到根的路径和,就是这个询问的答案

然后我们可以把每组询问拆成两个,变成ans[R]-ans[L-1]

然后就可以离线,把0~n-1一个一个往里加,每次把它到根的路径+1,然后查询对应的那些询问就可以了


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> 
#define N 100010
using namespace std;
int to[N],nxt[N],pre[N],fa[N],cnt;
void ae(int ff,int tt)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
}
int d[N],zs[N],siz[N];
void build1(int x)
{
	int maxn=0,maxb=0;
	int i,j;
	siz[x]=1;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		d[j]=d[x]+1;
		fa[j]=x;
		build1(j);
		siz[x]+=siz[j];
		if(siz[j]>maxn)
		{
			maxn=siz[j];
			maxb=j;
		}
	}
	zs[x]=maxb;
}
int top[N],sit[N],fan[N],cn;
void make(int x,int tt)
{
	cn++;sit[x]=cn;fan[cn]=x;
	top[x]=tt;
	int i,j;
	if(zs[x]) make(zs[x],tt);
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==zs[x]) continue;
		make(j,j);
	}
}
struct ppp{int x,z,num,v;}b[N];
bool cmp(ppp x,ppp y){return x.x<y.x;}
int ans[N];
int l[N<<2],r[N<<2],sum[N<<2],t[N<<2];
void pup(int x){sum[x]=sum[x<<1]+sum[x<<1|1];}
void pud(int x)
{
	sum[x<<1]+=(r[x<<1]-l[x<<1]+1)*t[x];
	sum[x<<1|1]+=(r[x<<1|1]-l[x<<1|1]+1)*t[x];
	t[x<<1]+=t[x];
	t[x<<1|1]+=t[x];
	t[x]=0;
}
void build(int now,int ll,int rr)
{
	l[now]=ll;r[now]=rr;
	if(ll==rr) return;
	int mid=(ll+rr)>>1;
	build(now<<1,ll,mid);
	build(now<<1|1,mid+1,rr);
}
void change(int now,int ll,int rr)
{
	if(l[now]==ll&&r[now]==rr)
	{
		t[now]++;
		sum[now]+=r[now]-l[now]+1;
		return;
	}
	if(t[now]) pud(now);
	int mid=(l[now]+r[now])>>1;
	if(rr<=mid) change(now<<1,ll,rr);
	else if(ll>mid) change(now<<1|1,ll,rr);
	else change(now<<1,ll,mid),change(now<<1|1,mid+1,rr);
	pup(now);
}
int check(int now,int ll,int rr)
{
	if(l[now]==ll&&r[now]==rr) return sum[now];
	if(t[now]) pud(now);
	int mid=(l[now]+r[now])>>1;
	if(rr<=mid) return check(now<<1,ll,rr);
	else if(ll>mid) return check(now<<1|1,ll,rr);
	else return check(now<<1,ll,mid)+check(now<<1|1,mid+1,rr);
}
void changeit(int x)
{
	while(x)
	{
		change(1,sit[top[x]],sit[x]);
		x=fa[top[x]];
	}
}
int checkit(int x)
{
	int ans=0;
	while(x)
	{
		ans+=check(1,sit[top[x]],sit[x]);
		x=fa[top[x]];
	}
	return ans;
}
int mod=201314;
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	int i,j,x,y,z;
	for(i=2;i<=n;i++)
	{
		scanf("%d",&x);
		ae(x+1,i);
	}
	build1(1);
	make(1,1);
	for(i=1;i<=q;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		x++;y++;z++;
		b[2*i-1]=(ppp){x-1,z,i,-1};
		b[2*i]=(ppp){y,z,i,1};
	}
	sort(b+1,b+2*q+1,cmp);
	j=1;
	while(b[j].x==0) j++;
	build(1,1,n);
	for(i=1;i<=n;i++)
	{
		changeit(i);
		while(b[j].x==i)
		{
			ans[b[j].num]+=b[j].v*checkit(b[j].z);
			j++;
		}
	}
	for(i=1;i<=q;i++)
	printf("%d\n",ans[i]%mod);
}

你可能感兴趣的:(树链剖分,bzoj,离线处理,省选)