【题目描述】
给出一个n个节点的有根树(编号为0到n-1,根节点为0)。一个点的深度定义为这个节点到根的距离+1。
设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先。
有q次询问,每次询问给出l r z,求
∑
l
<
=
i
<
=
r
d
e
p
[
L
C
A
(
i
,
z
)
]
\sum_{l<=i<=r}dep[LCA(i,z)]
l<=i<=r∑dep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
【输入】
第一行2个整数n q。
接下来n-1行,分别表示点1到点n-1的父节点编号。
接下来q行,每行3个整数l r z。
【输出】
输出q行,每行表示一个询问的答案。每个答案对201314取模输出
【样例输入】
5 2
0
0
1
1
1 4 3
1 4 2
【】样例输出】
8
5
【提示】
共5组数据,n与q的规模分别为10000,20000,30000,40000,50000。
【思路】
这道题的做法不得不说很巧妙。
我们可以构造一个等价问题。如果我们把u到根节点经过的每一个点的权值加一,那么我们可以发现v到根节点经过的点的权值之和就是dep[lca(u,v)]。这是一个十分优美的性质,因为它不仅满足区间加法,甚至满足区间减法。因此我们对于每一个询问l,r,z,我们可以利用前缀和思想得到ans[l,r,z]=ans[1,r,z]-ans[1,l-1,z]。那么现在的问题就只剩下如何得到z到根节点的权值和以及修改节点到1的权值。显然,修改和维护路径信息,我们就使用树链剖分+线段树了。
代码:
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#define re register
#define LL long long
#define lc (p<<1)
#define rc (p<<1|1)
#define len(p) (t[p].r-t[p].l+1)
using namespace std;
long long n,m,a,b,c;
struct node{
long long u,v;
}e[200001];
struct tree{
long long l,r;
long long sum;
long long laz;
}t[400001];
long long f[100001];
long long nxp[200001];
long long cnt=0,tot=0;
inline void add(long long u,long long v)
{
e[++cnt].u=u;
e[cnt].v=v;
nxp[cnt]=f[u];
f[u]=cnt;
}
long long son[100001];
long long siz[100001];
long long dep[100001];
long long fa[100001];
long long top[100001];
long long seg[100001];
long long rev[100001];
void dfs1(long long u,long long ff)
{
dep[u]=dep[ff]+1;
fa[u]=ff;
siz[u]=1;
for(long long re i=f[u];i;i=nxp[i])
{
long long v=e[i].v;
if(dep[v])continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(long long u)
{
if(son[u])
{
seg[son[u]]=++tot;
rev[tot]=son[u];
top[son[u]]=top[u];
dfs2(son[u]);
}
for(long long re i=f[u];i;i=nxp[i])
{
long long v=e[i].v;
if(top[v])continue;
seg[v]=++tot;
rev[tot]=v;
top[v]=v;
dfs2(v);
}
}
void pushup(long long p){t[p].sum=t[lc].sum+t[rc].sum;}
void pushnow(long long p,long long v){t[p].laz+=v;t[p].sum+=len(p)*v;}
void pushdown(long long p)
{
if(t[p].laz)
{
pushnow(lc,t[p].laz);
pushnow(rc,t[p].laz);
t[p].laz=0;
}
}
void build(long long p,long long l,long long r)
{
t[p].l=l;
t[p].r=r;
if(l==r)return;
long long mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
struct que{
long long z,opt,id;
long long sum[2];
}q[100001];
vector<que>p[50001];
void change(long long p,long long ql,long long qr)
{
long long l=t[p].l;
long long r=t[p].r;
if(ql<=l&&r<=qr)
{
pushnow(p,1);
return;
}
pushdown(p);
long long mid=(l+r)>>1;
if(ql<=mid)change(lc,ql,qr);
if(qr>mid)change(rc,ql,qr);
pushup(p);
}
long long sum=0;
void ask(long long p,long long ql,long long qr)
{
long long l=t[p].l;
long long r=t[p].r;
if(ql<=l&&qr>=r)
{
sum+=t[p].sum;
return;
}
pushdown(p);
long long mid=(l+r)>>1;
if(ql<=mid)ask(lc,ql,qr);
if(qr>mid)ask(rc,ql,qr);
}
void update(long long x)
{
long long fx=top[x];
while(fx!=1)
{
change(1,seg[fx],seg[x]);
x=fa[fx];
fx=top[x];
}
change(1,seg[1],seg[x]);
}
void query(long long x)
{
sum=0;
long long fx=top[x];
while(fx!=1)
{
ask(1,seg[fx],seg[x]);
x=fa[fx];
fx=top[x];
}
ask(1,seg[1],seg[x]);
}
int main()
{
scanf("%lld%lld",&n,&m);
for(long long re i=2;i<=n;i++)
{
scanf("%lld",&a);a++;
add(a,i);
add(i,a);
}
tot=top[1]=dep[1]=rev[1]=seg[1]=1;
build(1,1,n);
dfs1(1,0);
dfs2(1);
for(long long re i=1;i<=m;i++)
{
long long l,r;
scanf("%lld%lld%lld",&l,&r,&c);l++,r++,c++;
q[i].z=c;
q[i].id=i;
q[i].opt=0;
p[l-1].push_back(q[i]);
q[i].opt=1;
p[r].push_back(q[i]);
}
for(long long re i=0;i<=n;i++)
{
if(i>0)
update(i);
for(long long re j=0;j<p[i].size();j++)
{
query(p[i][j].z);
q[p[i][j].id].sum[p[i][j].opt]=sum;
}
}
for(long long re i=1;i<=m;i++)printf("%lld\n",(q[i].sum[1]-q[i].sum[0])%201314);
}