题目大意
一棵有
n
个节点的树(根为
有
q
个询问,形如
∑x=lrdeep(lca(x,z))
答案对 201314 取模。
1≤n,q≤50000
题目分析
求区间和太麻烦,我们差分一下,变成求前缀和,那么每个询问就拆分成两个前缀和询问。
统计
lca
的深度和看起来还是很棘手,我们换个角度想:如果我们将
x
到根的路径上所有点权值都赋值为
类似的,我们离线处理,将询问的
z
挂在节点
使用树链剖分,在
P.S.
根据Werkeytom_FTD的说法,强制在线的话可以使用可持久化线段树解决。
代码实现
#include <iostream>
#include <cstdio>
#include <cctype>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=(ch=='-')?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int P=201314;
const int N=50500;
const int Q=50500;
const int A=Q<<1;
struct D
{
int u,id;
bool tp;
D(int u0=0,int id0=0,bool tp0=0){u=u0,id=id0,tp=tp0;}
}aim[A];
struct segment_tree
{
int sum[N<<2],tag[N<<2];
void add(int x,int l,int r,int delta){(sum[x]+=1ll*(r-l+1)*delta%P)%=P,(tag[x]+=delta)%=P;}
void clear(int x,int l,int r)
{
if (tag[x])
{
if (l!=r)
{
int mid=l+r>>1;
add(x<<1,l,mid,tag[x]),add(x<<1|1,mid+1,r,tag[x]);
}
tag[x]=0;
}
}
void modify(int x,int st,int en,int l,int r,int delta)
{
clear(x,l,r);
if (st==l&&en==r)
{
add(x,l,r,delta);
clear(x,l,r);
return;
}
int mid=l+r>>1;
if (en<=mid) modify(x<<1,st,en,l,mid,delta);
else if (mid+1<=st) modify(x<<1|1,st,en,mid+1,r,delta);
else modify(x<<1,st,mid,l,mid,delta),modify(x<<1|1,mid+1,en,mid+1,r,delta);
sum[x]=(sum[x<<1]+sum[x<<1|1])%P;
}
int query(int x,int st,int en,int l,int r)
{
clear(x,l,r);
if (st==l&&en==r) return sum[x];
int mid=l+r>>1;
if (en<=mid) return query(x<<1,st,en,l,mid);
else if (mid+1<=st) return query(x<<1|1,st,en,mid+1,r);
else return (query(x<<1,st,mid,l,mid)+query(x<<1|1,mid+1,en,mid+1,r))%P;
}
}t;
int last[N],tov[N],next[N],DFN[N],fa[N],orig[N],hea[N],size[N],head[N];
int n,q,tot,cnt,idx;
int nxt[A];
int ans[Q];
void insert(int x,int y)
{
tov[++tot]=y,next[tot]=last[x],last[x]=tot;
}
void hang(int x,D y)
{
aim[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
}
void dfs(int x)
{
size[x]=1;
for (int i=last[x],y;i;i=next[i])
dfs(y=tov[i]),size[x]+=size[y];
}
void build(int x,int og)
{
DFN[x]=++idx,orig[x]=og;
for (int i=last[x];i;i=next[i])
if (!hea[x]||size[hea[x]]<size[tov[i]])
hea[x]=tov[i];
if (!hea[x]) return;
build(hea[x],og);
for (int i=last[x];i;i=next[i])
if (tov[i]!=hea[x]) build(tov[i],tov[i]);
}
void fillpath(int x)
{
for (int y=orig[x];x;y=orig[x=fa[y]])
t.modify(1,DFN[y],DFN[x],1,idx,1);
}
int sumpath(int x)
{
int ret=0;
for (int y=orig[x];x;y=orig[x=fa[y]])
(ret+=t.query(1,DFN[y],DFN[x],1,idx))%=P;
return ret;
}
void calc(int x)
{
for (int i=head[x];i;i=nxt[i])
{
D qy=aim[i];
(((ans[qy.id]+=(qy.tp?1:-1)*sumpath(qy.u))%=P)+=P)%=P;
}
}
int main()
{
freopen("lca.in","r",stdin),freopen("lca.out","w",stdout);
n=read(),q=read();
for (int i=2;i<=n;i++) fa[i]=read()+1,insert(fa[i],i);
dfs(1),build(1,1);
for (int i=1,l,r,z;i<=q;i++)
{
l=read()+1,r=read()+1,z=read()+1;
if (l-1) hang(l-1,D(z,i,0));
hang(r,D(z,i,1));
}
for (int i=1;i<=n;i++) fillpath(i),calc(i);
for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
fclose(stdin),fclose(stdout);
return 0;
}