【题解】
若将0到u路径上所有点标记,则deep[LCA(u,v)]等于从v上溯到的第一个被标记点的deep,而再往上的话一直到根,经过的点都是被标记点
由前缀和的思想,将0到u路径上所有点权值设为1,其他点权值为0,那么deep[LCA(u,v)]等于SUM(0到v路径上的点权和)
再进一步想,deep[LCA(u1,v)]+deep[LCA(u2,v)]怎么求?
1.可以将0到v路径上的点权设为1,再分别求u1,u2到根的点权和,但这样无法优化时间,相当于暴力
2.也可以将0到u1路径上的点权加1,再将0到u2路径上的点权加1,然后求v到根的点权和,因为每个值为1的权相当于深度的一步,是可叠加的
按照方法2,在 将0至ui的路径上的结点权值加1 之后(ui取遍[l,r]),就可以一并求出sigma_{l<=i<=r}dep[LCA(i,z)]了
对于q次询问,不能每次都把点权变来变去,这里再次利用叠加性与前缀和思想,将ans[l,r]拆开,转换为ans[0,r]-ans[0,l-1],求解两个子问题即可
这样,我们可以预处理完1~n-1每个点的影响:每个点到根所经过的点的权值加1,链剖+线段树维护路径点权和
在这个过程中,2*q个被拆开的询问离线处理即可,比如询问ans[0,x]:在处理完点x后,求它到根的路径上的权值和,并记录下这来自第几个问题就行了
本题中的重要思想:区间查询都可以转化为前缀和的差值,还有如何将LCA问题转化为维护树上路径信息的问题
【代码】
#include<stdio.h>
#include<stdlib.h>
#include<vector>
#define MOD 201314
using namespace std;
typedef long long LL;
vector<int> G[50005],A[50005],N[50005];
int fa[50005]={0},size[50005]={0},son[50005]={0},top[50005]={0},pos[50005]={0},p[50005]={0};
LL sumv[200000]={0},addv[200000]={0},Q[50005][5]={0};
int n,e=0,tot=0;
LL jdz(LL x)
{
if(x<0) x=-x;
return x;
}
void dfs1(int x)
{
int i;
size[x]=1;
for(i=0;i<G[x].size();i++)
{
dfs1(G[x][i]);
size[x]+=size[G[x][i]];
if(son[x]==0||size[son[x]]<size[G[x][i]]) son[x]=G[x][i];
}
}
void dfs2(int x,int t)
{
int i;
top[x]=t;
pos[x]=++tot;
if(son[x]!=0) dfs2(son[x],t);
for(i=0;i<G[x].size();i++)
if(G[x][i]!=son[x]) dfs2(G[x][i],G[x][i]);
}
void xg(LL p,int x,int y,int o,int left,int right)
{
int mid=(left+right)/2;
if(x<=left&&right<=y) addv[o]+=p;
else
{
if(x<=mid) xg(p,x,y,o*2,left,mid);
if(y>mid) xg(p,x,y,o*2+1,mid+1,right);
}
if(left<right) sumv[o]=sumv[o*2]+sumv[o*2+1]+addv[o]*(LL)(right-left+1);
else sumv[o]+=p;
}
LL cx(int x,int y,int o,int left,int right,LL add)
{
LL ans=0;
int mid=(left+right)/2;
if(x<=left&&right<=y) return sumv[o]+add*(LL)(right-left+1);
add+=addv[o];
if(x<=mid) ans+=cx(x,y,o*2,left,mid,add);
if(y>mid) ans+=cx(x,y,o*2+1,mid+1,right,add);
return ans;
}
void update(int x)
{
int tx=top[x];
while(tx!=0)
{
xg(1,pos[tx],pos[x],1,1,n);
x=fa[tx];
tx=top[x];
}
xg(1,pos[0],pos[x],1,1,n);
}
LL getsum(int x)
{
LL ans=0;
int tx=top[x];
while(tx!=0)
{
ans=ans+cx(pos[tx],pos[x],1,1,n,0);
x=fa[tx];
tx=top[x];
}
return ans+cx(pos[0],pos[x],1,1,n,0);
}
int main()
{
int q,i,j,l,r,z;
scanf("%d%d",&n,&q);
for(i=1;i<n;i++)
{
scanf("%d",&fa[i]);
G[fa[i]].push_back(i);
}
dfs1(0);
dfs2(0,0);
for(i=1;i<=q;i++)
{
scanf("%d%d%d",&l,&r,&z);
if(l>0)
{
A[l-1].push_back(z);
N[l-1].push_back(i);
}
A[r].push_back(z);
N[r].push_back(i);
}
for(i=0;i<n;i++)
{
update(i);
for(j=0;j<A[i].size();j++)
Q[N[i][j]][++p[N[i][j]]]=getsum(A[i][j]);
}
for(i=1;i<=q;i++)
printf("%lld\n",jdz(Q[i][1]-Q[i][2])%MOD);
return 0;
}
注意最后求答案时Q[i][j]不能是mod过的,否则会导致错误,所以Q[i][j]需定义成long long类型
或者Q[i][j]是mod过的也行,但需要记录这个Q[i][j]属于ans[0,l-1]还是ans[0,r]