3626: [LNOI2014]LCA
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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;
}