题目描述:
给出一个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<=rdep[LCA(i,z)]。
(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)
n,q<=50000
题目分析:
高超的技巧:
求两点x,y的lca的深度,可以把x到根的路径上的点全部打上标记,lca的深度就是y往上遇到的第一个打了标记的点(记作z)的深度,而从z往上的每个点都是打了标记的,所以等价于y往上直到根所遇到的打了标记的点的个数。
这道题求区间,显然不能每次把区间内的每个点都往上加一遍,考虑差分后离线,把询问拆成 [1,l-1],z 和 [1,r],z ,从1到n加点,加入点i时把i到根的路径上的点权+1,询问[1,i],z时统计z到根的路径和即可
树上路径加和路径求和可以用树链剖分,dfs一下可以把一条重链上的点划成一段连续的区间,就可以用线段树维护每条重链上的信息了。
#include<cstdio>
#include<algorithm>
#define maxn 50005
using namespace std;
const int mod = 201314;
int n,m,pos[maxn],pt,ans[maxn],fa[maxn];
int siz[maxn],son[maxn],top[maxn];
int fir[maxn],nxt[maxn],to[maxn],tot;
inline void line(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
struct Q{
int r,z,flg,id;
Q(){}
Q(int _r,int _z,int _flg,int _id){r=_r,z=_z,flg=_flg,id=_id;}
bool operator < (const Q &p)const{return r<p.r;}
}q[maxn<<1];
struct segment_tree{
int s[maxn<<2],tag[maxn<<2];
void pushdown(int i,int l,int r)
{
if(!tag[i]) return;
int mid=(l+r)>>1;
s[i<<1]=(s[i<<1]+1ll*(mid-l+1)*tag[i])%mod;
s[i<<1|1]=(s[i<<1|1]+1ll*(r-mid)*tag[i])%mod;
tag[i<<1]+=tag[i],tag[i<<1|1]+=tag[i];
tag[i]=0;
}
void insert(int i,int l,int r,int x,int y)
{
if(x<=l&&r<=y) {s[i]=(s[i]+r-l+1)%mod,tag[i]++;return;}
pushdown(i,l,r);
int mid=(l+r)>>1;
if(x<=mid) insert(i<<1,l,mid,x,y);
if(y>mid) insert(i<<1|1,mid+1,r,x,y);
s[i]=(s[i<<1]+s[i<<1|1])%mod;
}
int query(int i,int l,int r,int x,int y)
{
if(x<=l&&r<=y) return s[i];
pushdown(i,l,r);
int mid=(l+r)>>1,ret=0;
if(x<=mid) ret+=query(i<<1,l,mid,x,y);
if(y>mid) ret+=query(i<<1|1,mid+1,r,x,y);
return ret;
}
}seg;
void dfs1(int u)
{
siz[u]=1;
for(int i=fir[u];i;i=nxt[i]){
dfs1(to[i]);
siz[u]+=siz[to[i]];
if(!son[u]||siz[son[u]]<siz[to[i]]) son[u]=to[i];
}
}
void dfs2(int u)
{
pos[u]=++pt;
if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
for(int i=fir[u];i;i=nxt[i]) if(to[i]!=son[u]) top[to[i]]=to[i],dfs2(to[i]);
}
void update(int x)
{
while(top[x]!=top[0]){
seg.insert(1,1,n,pos[top[x]],pos[x]);
x=fa[top[x]];
}
seg.insert(1,1,n,pos[top[0]],pos[x]);
}
int solve(int x){
int s=0;
while(top[x]!=top[0]){
s+=seg.query(1,1,n,pos[top[x]],pos[x]);
x=fa[top[x]];
}
return s+seg.query(1,1,n,pos[top[0]],pos[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++) scanf("%d",&fa[i]),line(fa[i],i);
for(int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&x,&y,&z),q[i*2-1]=Q(x-1,z,-1,i),q[i*2]=Q(y,z,1,i);
sort(q+1,q+1+2*m);
dfs1(0),top[0]=0,dfs2(0);
for(int i=1,j=-1;i<=2*m;i++){
if(q[i].r==-1) continue;
while(j<q[i].r) update(++j);
ans[q[i].id]=(ans[q[i].id]+solve(q[i].z)*q[i].flg)%mod;
}
for(int i=1;i<=m;i++) printf("%d\n",(ans[i]+mod)%mod);
}