【BZOJ3626】LCA(树链剖分,Link-Cut Tree)

【BZOJ3626】LCA(树链剖分,Link-Cut Tree)

题面

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。

题解

我不会做的一道神题
OrzZsyDalao,看一眼就会做

如果暴力求,,,还是挺好的呀

咳咳,看正解
画画图
对于区间L~R,把每个点到根节点的路径全部+1
此时累加Z到根节点的路径权值和,发现就是答案

为啥呢
首先,我们可以知道LCA一定在Z到根节点的路径上
其次,深度就是LCA到根节点的路径长度
如果像上面那样计算,因为整个路径上都加了1
相当于加了整个LCA的深度
因此这样做就是对的

所以,直接离线处理就好了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define lson (t[x].ch[0])
#define rson (t[x].ch[1])
#define MAX 100000
#define MOD 201314
inline int read()
{
	int x=0,t=1;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=-1,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return x*t;
}
struct Node
{
	int ch[2],ff;
	int rev,pls;
	int size,sum,v;
}t[MAX];
int S[MAX],top,n,Q;
bool isroot(int x){return t[t[x].ff].ch[0]!=x&&t[t[x].ff].ch[1]!=x;}
void pushup(int x)
{
	t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
	t[x].sum=(t[t[x].ch[0]].sum+t[t[x].ch[1]].sum+t[x].v)%MOD;
}
void Reverse(int x){swap(lson,rson);t[x].rev^=1;}
void pushdown(int x)
{
	if(t[x].rev)
	{
		if(lson)Reverse(lson);
		if(rson)Reverse(rson);
		t[x].rev^=1;
	}
	if(t[x].pls)
	{
		if(lson)
		{
			(t[lson].v+=t[x].pls)%=MOD;
			(t[lson].sum+=t[x].pls*t[lson].size%MOD)%=MOD;
			t[lson].pls=(t[lson].pls+t[x].pls)%MOD;
		}
		if(rson)
		{
			(t[rson].v+=t[x].pls)%=MOD;
			(t[rson].sum+=t[x].pls*t[rson].size%MOD)%=MOD;
			t[rson].pls=(t[rson].pls+t[x].pls)%MOD;
		}
		t[x].pls=0;
	}
}
void rotate(int x)
{
    int y=t[x].ff,z=t[y].ff;
    int k=t[y].ch[1]==x;
    if(!isroot(y))t[z].ch[t[z].ch[1]==y]=x;t[x].ff=z;
    t[y].ch[k]=t[x].ch[k^1];t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y;t[y].ff=x;
    pushup(y);pushup(x);
}
void Splay(int x)
{
    S[top=1]=x;
    for(int i=x;!isroot(i);i=t[i].ff)S[++top]=t[i].ff;
    while(top)pushdown(S[top--]);
    while(!isroot(x))
    {
        int y=t[x].ff,z=t[y].ff;
        if(!isroot(y))
            (t[y].ch[1]==x)^(t[z].ch[1]==y)?rotate(x):rotate(y);
        rotate(x);
    }
}
void access(int x){for(int y=0;x;y=x,x=t[x].ff)Splay(x),t[x].ch[1]=y,pushup(x);}
void makeroot(int x){access(x);Splay(x);Reverse(x);}
void split(int x,int y){makeroot(x);access(y);Splay(y);}
void cut(int x,int y){split(x,y);t[y].ch[0]=t[x].ff=0;pushup(y);}
void link(int x,int y){makeroot(x);t[x].ff=y;}
struct Ask
{
	int id,z,i,opt;
}q[MAX*2];
bool cmp(Ask a,Ask b){return a.i<b.i;}
int ans[MAX],tot;
int main()
{
	n=read();Q=read();
	for(int i=2;i<=n;++i)
	{
		int u=read()+1;
		link(i,u);
	}
	for(int i=1;i<=Q;++i)
	{
		int l=read(),r=read()+1,z=read()+1;
		++tot;q[tot].id=q[tot+1].id=i;
		q[tot].z=q[tot+1].z=z;
		q[tot].i=l;q[tot+1].i=r;
		q[tot].opt=-1;q[++tot].opt=1;
	}
	sort(&q[1],&q[tot+1],cmp);
	int pos=1;
	while(!q[pos].i&&pos<tot)++pos;
	for(int i=1;i<=n;++i)
	{
		split(i,1);
		t[1].v=(t[1].v+1)%MOD;
		t[1].sum=(t[1].sum+t[1].size)%MOD;
		t[1].pls=(t[1].pls+1)%MOD;
		while(q[pos].i==i&&pos<=tot)
		{
			split(q[pos].z,1);
			ans[q[pos].id]=(ans[q[pos].id]+q[pos].opt*t[1].sum%MOD+MOD)%MOD;
			++pos;
		}
	}
	for(int i=1;i<=Q;++i)printf("%d\n",ans[i]%MOD);
	return 0;
}

posted @ 2017-12-31 21:36  小蒟蒻yyb  阅读(432)  评论(0编辑  收藏  举报