这里是一种动态点分治的解法(不过听说树剖+主席树更快?)。
首先先考虑除去年龄的限制这道题怎么做。也就是给你一棵树,每次询问一个点到所有其他点的距离和。
由于路径问题不太关系树的形态,并且问的又是一个点和整棵树之间的关系,所以可以考虑动态点分治:
- 每个点保存它在点分树内的子树的信息
- 设(dis1[i])表示(i)子树内所有点到它的距离之和,(dis2[i])表示(i)子树内所有点到(fa[i])(依然是点分树上的父亲)的距离
- 查询就直接跳点分树好了
现在带上年龄的限制,我们依然可以“每个点保存它在点分树内的子树的信息”,我们可以用一个(vector)把一棵子树里的点全部保存下来,按年龄排个序,求一下(dis1)和(dis2)的后(前)缀和,最后询问的时候二分一下就好了。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N=500009,INF=1<<30;
int n,Q,age[N],log[N],A,head[N],cnt,F[N],root,R[N],L[N],Index,DFN[N],rev[N];
struct D
{
LL dis1,dis2;
int age;
bool operator < (const D &A)const
{
return age<A.age;
}
};
vector <D> b[N];
struct Edge
{
int nxt,to;
}g[N*2];
struct G
{
int head[N],cnt,Euler[N],Index,Fst[N],f[N][30],dep[N],siz[N],del[N];
LL dis[N];
struct Edge
{
int nxt,to,w;
}g[N*2];
void add(int from,int to,int w)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
g[cnt].w=w;
head[from]=cnt;
}
void dfs(int x,int fa)
{
Euler[++Index]=x,f[Index][0]=x,Fst[x]=Index;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
dep[v]=dep[x]+1,dis[v]=dis[x]+g[i].w;
dfs(v,x);
Euler[++Index]=x,f[Index][0]=x;
}
}
int LCA(int x,int y)
{
if(Fst[x]>Fst[y])
swap(x,y);
x=Fst[x],y=Fst[y];
int k=log[y-x+1];
return dep[f[x][k]]<dep[f[y-(1<<k)+1][k]]?f[x][k]:f[y-(1<<k)+1][k];
}
LL Get_Dis(int x,int y)
{
return dis[x]+dis[y]-2*dis[LCA(x,y)];
}
void DFS(int x,int fa)
{
siz[x]=1;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
DFS(v,x);
siz[x]+=siz[v];
}
}
int Get_Weight(int x)
{
DFS(x,-1);
int k=siz[x]/2,fa=-1;
while(1)
{
int tmp=0;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||del[v])
continue;
if(siz[tmp]<siz[v])
tmp=v;
}
if(siz[tmp]<=k)
return x;
fa=x,x=tmp;
}
}
void work()
{
dfs(1,-1);
for (int j=1;1<<j<=Index;j++)
for (int i=1;i+(1<<j)<=Index;i++)
if(dep[f[i][j-1]]<dep[f[i+(1<<j-1)][j-1]])
f[i][j]=f[i][j-1];
else
f[i][j]=f[i+(1<<j-1)][j-1];
}
}T;
void add(int from,int to)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void init()
{
log[0]=-1;
for (int i=1;i<=N-9;i++)
log[i]=log[i>>1]+1;
scanf("%d %d %d",&n,&Q,&A);
for (int i=1;i<=n;i++)
scanf("%d",&age[i]);
for (int i=1;i<n;i++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
T.add(x,y,z),T.add(y,x,z);
}
T.work();
}
void build(int fa)
{
T.del[fa]=1,DFN[fa]=++Index,L[fa]=Index,rev[Index]=fa;
for (int i=T.head[fa];i;i=T.g[i].nxt)
{
int v=T.g[i].to;
if(v==fa||T.del[v])
continue;
int w=T.Get_Weight(v);
F[w]=fa,add(fa,w),add(w,fa);
build(w);
}
R[fa]=Index;
//printf("%d %d %d
",fa,L[fa],R[fa]);
}
void print(int x,int fa)
{
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
printf("%d %d
",x,v);
print(v,x);
}
}
void dfs(int x,int fa)
{
b[x].push_back((D){0,fa!=-1?T.Get_Dis(fa,x):0,age[x]});
b[x].push_back((D){0,0,INF});
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
for (int j=L[v];j<=R[v];j++)
{
int J=rev[j];
b[x].push_back( ( D ) { T.Get_Dis(x,J) , fa!=-1?T.Get_Dis(fa,J):0 , age[J] } );
}
dfs(v,x);
}
sort(b[x].begin(),b[x].end());
for (int i=b[x].size()-2;i>=0;i--)
b[x][i].dis1+=b[x][i+1].dis1,b[x][i].dis2+=b[x][i+1].dis2;
//printf("now_%d
",x);
//for (int i=0;i<b[x].size();i++)
//printf("%d ",b[x][i].age);puts("");
}
LL Query(int x,int l,int r)
{
vector <D> ::iterator L,R;
LL ans=0;
for (int i=x;i;i=F[i])
{
L=lower_bound(b[i].begin(),b[i].end(),(D){0,0,l});
R=upper_bound(b[i].begin(),b[i].end(),(D){0,0,r});
ans+=(L->dis1-R->dis1)+1LL*(R-L)*T.Get_Dis(x,i);
if(F[i])
ans-=1LL*(R-L)*T.Get_Dis(x,F[i])+L->dis2-R->dis2;
}
return ans;
}
void work()
{
root=T.Get_Weight(1);
//puts("pipi");
build(root);
//puts("houhou");
//print(root,-1);
dfs(root,-1);
LL last=0;
for (int _=1;_<=Q;_++)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
int L=min((y+last)%A,(z+last)%A),R=max((y+last)%A,(z+last)%A);
if(L>R)
swap(L,R);
//printf("L=%d R=%d
",L,R);
printf("%lld
",last=Query(x,L,R));
}
}
int main()
{
init();
work();
return 0;
}