bzoj 3626: [LNOI2014]LCA(离线差分+树链剖分)

3626: [LNOI2014]LCA

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 2885   Solved: 1133
[ Submit][ Status][ Discuss]

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


考虑暴力

假设每次查询只有一个点x而不是一段区间,那么只要将x点到根这段路径上所有的点权值+1就好了

这样对于树上所有的点z,z与x的最近公共祖先深度就是z到根这段路程的权值和

既然查询的是区间,那么对于区间中所有的节点,它们到根的这段路程都额外+1,然后再查询就好了

每次修改和查询树链剖分的话都可以直接线段树维护,复杂度(log²n)


考虑离线,编号从1到n,依次添加它们到根的这段路径的权值,当添加完第x个点之后,

z到根的路径权值之和就是答案∑LCA(i, z)  (1<=i<=x),存下来就好

那么对于每个查询∑LCA(i, z)  (l<=i<=r),答案就是∑LCA(i, z)  (1<=i<=r) - ∑LCA(i, z)  (1<=i<=l-1)

#include<stdio.h>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
vector<LL> G[50005];
typedef struct Res
{
	LL x;
	LL aim;
	LL id, t;
	bool operator < (const Res &b) const
	{
		if(x<b.x)
			return 1;
		return 0;
	}
}Res;
Res s[100005];
LL n, siz[50005], son[50005], dep[50005], fa[50005];
LL k, cnt, top[50005], rak[50005], id[50005], ans[50005][2], tre[222222], temp[222222];
void Sech1(LL u, LL p)
{
	LL i, v;
	fa[u] = p, dep[u] = dep[p]+1;
	siz[u] = 1;
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(v==p)
			continue;
		Sech1(v, u);
		siz[u] += siz[v];
		if(son[u]==0 || siz[v]>siz[son[u]])
			son[u] = v;
	}
}
void Sech2(LL u, LL p)
{
	LL v, i;
	top[u] = p;
	rak[u] = ++k, id[k] = u;
	if(son[u]==0)
		return;
	Sech2(son[u], p);
	for(i=0;i<G[u].size();i++)
	{
		v = G[u][i];
		if(son[u]==v || fa[u]==v)
			continue;
		Sech2(v, v);
	}
}
void Update(LL l, LL r, LL x, LL a, LL b);
void Lazy(LL l, LL r, LL x);
LL Query(LL l, LL r, LL x, LL a, LL b);
void Tre_Update(LL x)
{
	while(x)
	{
		Update(1, n, 1, rak[top[x]], rak[x]);
		x = fa[top[x]];
	}
}
LL Tre_Query(LL x)
{
	LL ans = 0;
	while(x)
	{
		ans += Query(1, n, 1, rak[top[x]], rak[x]);
		x = fa[top[x]];
	}
	return ans;
}
int main(void)
{
	LL q, i, x, y, z, p;
	scanf("%lld%lld", &n, &q);
	for(i=2;i<=n;i++)
	{
		scanf("%lld", &x);
		G[x+1].push_back(i);
		G[i].push_back(x+1);
	}
	Sech1(1, 0);
	Sech2(1, 1);
	for(i=1;i<=q;i++)
	{
		scanf("%lld%lld%lld", &x, &y, &z);
		s[++cnt].id = i, s[cnt].aim = z+1, s[cnt].x = x, s[cnt].t = 0;
		s[++cnt].id = i, s[cnt].aim = z+1, s[cnt].x = y+1, s[cnt].t = 1;
	}
	sort(s+1, s+cnt+1);
	p = 1;
	for(i=1;i<=n;i++)
	{
		Tre_Update(i);
		while(s[p].x<i)
			p++;
		while(s[p].x==i)
		{
			ans[s[p].id][s[p].t] = Tre_Query(s[p].aim);
			p++;
		}
	}
	for(i=1;i<=q;i++)
		printf("%lld\n", (ans[i][1]-ans[i][0])%201314);
	return 0;
}

void Lazy(LL l, LL r, LL x)
{
	LL m;
	m = (l+r)/2;
	tre[x*2] += temp[x]*(m-l+1);
	tre[x*2+1] += temp[x]*(r-m);
	if(l!=m)
		temp[x*2] += temp[x];
	if(r!=m+1)
		temp[x*2+1] += temp[x];
	temp[x] = 0;
}
void Update(LL l, LL r, LL x, LL a, LL b)
{
	LL m;
	if(l>=a && r<=b)
	{
		tre[x] += r-l+1;
		if(l!=r)
			temp[x]++;
		return;
	}
	m = (l+r)/2;
	if(temp[x])
		Lazy(l, r, x);
	if(a<=m)
		Update(l, m, x*2, a, b);
	if(b>=m+1)
		Update(m+1, r, x*2+1, a, b);
	tre[x] = tre[x*2]+tre[x*2+1];
}
LL Query(LL l, LL r, LL x, LL a, LL b)
{
	LL m, sum = 0;
	if(l>=a && r<=b)
		return tre[x];
	m = (l+r)/2;
	if(temp[x])
		Lazy(l, r, x);
	if(a<=m)
		sum += Query(l, m, x*2, a, b);
	if(b>=m+1)
		sum += Query(m+1, r, x*2+1, a, b);
	return sum;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值