题目描述
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。
这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有
n
n
n 个地方,编号为
1
1
1 到
n
n
n 被
n
−
1
n-1
n−1 条带权的边连接起来。每个地方都住着一个妖怪,其中第
i
i
i 个地方的妖怪年龄是
x
i
x_i
xi。
妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3 3 3 。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 18 18 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 u u u ( u u u 为编号),然后在 u u u 开一家面向年龄在 L L L 到 R R R 之间(即年龄大于等于 L L L 小于等于 R R R)的妖怪的店。
也有可能 u u u 这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 L L L 到 R R R 之间的妖怪,到点 u u u 的距离的和是多少(妖怪到 u u u 的距离是该妖怪所在地方到 u u u 的路径上的边的权之和),幽香把这个称为这个开店方案的方便值。
幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
输入输出格式
输入格式:
第一行三个用空格分开的数
n
,
Q
n,Q
n,Q 和
A
A
A,表示树的大小、开店的方案个数和妖怪的年龄上限。
第二行nn 个用空格分开的数
x
1
,
x
2
,
…
,
x
n
x_1,x_2,\ldots,x_n
x1,x2,…,xn;
x
i
x_i
xi表示第
i
i
i 个地点妖怪的年龄,满足
0
≤
x
i
<
A
0\le x_i\lt A
0≤xi<A 。(年龄是可以为
0
0
0 的,例如刚出生的妖怪的年龄为
0
0
0。)
接下来 n − 1 n-1 n−1 行,每行三个用空格分开的数 a a a 、 b b b 、 c c c ,表示树上的顶点 a a a 和 b b b 之间有一条权为 c ( 1 ≤ c ≤ 1000 ) c(1\le c\le1000) c(1≤c≤1000) 的边,aa 和bb 是顶点编号。
接下来 Q Q Q 行,每行三个用空格分开的数 u , a , b u,a,b u,a,b 。
对于这 Q Q Q 行的每一行,用 a , b , A a,b,A a,b,A 计算出 L L L 和 R R R ,表示询问”在地方 u u u 开店,面向妖怪的年龄区间为 [ L , R ] [L,R] [L,R] 的方案的方便值是多少。
对于其中第 1 1 1 行, L L L 和 R R R 的计算方法为: L = m i n ( a % A , b % A ) L=min(a \% A,b \%A) L=min(a%A,b%A), R = m a x ( a % A , b % A ) R=max(a \% A,b \%A) R=max(a%A,b%A) 。
对于第
2
2
2 到第
Q
Q
Q 行,假设前一行得到的方便值为
a
n
s
ans
ans,那么当前行的
L
L
L 和
R
R
R 计算方法为:
L
=
m
i
n
(
(
a
+
a
n
s
)
%
A
,
(
b
+
a
n
s
)
%
A
)
L=min((a+ans) \% A,(b+ans) \%A)
L=min((a+ans)%A,(b+ans)%A),
R
=
m
a
x
(
(
a
+
a
n
s
)
%
A
,
(
b
+
a
n
s
)
%
A
)
R=max((a+ans) \% A,(b+ans) \%A)
R=max((a+ans)%A,(b+ans)%A)。
输出格式:
对于每个方案,输出一行表示方便值。
输入输出样例
输入样例#1:
10 10 10
0 0 7 2 1 4 7 7 7 9
1 2 270
2 3 217
1 4 326
2 5 361
4 6 116
3 7 38
1 8 800
6 9 210
7 10 278
8 9 8
2 8 0
9 3 1
8 0 8
4 2 7
9 7 3
4 7 0
2 2 7
3 2 1
2 3 4
输出样例#1:
1603
957
7161
9466
3232
5223
1879
1669
1282
0
说明
满足
n
≤
1.5
∗
1
0
5
,
Q
≤
2
∗
1
0
5
n\le1.5*10^5,Q\le2*10^5
n≤1.5∗105,Q≤2∗105。对于所有数据,满足
A
<
=
1
0
9
A<=10^9
A<=109。
分析:
我们可以对树进行动态树分治,每个点维护当前分治中心所有节点的年龄,到分治中心的距离。存进一个vector然后再排序跑前缀和。顺便维护到父亲分治中心的同样的东西。
对于一个询问。当前分治中心父亲除了当前块外的所有连通块的
[
L
,
R
]
[L,R]
[L,R]的距离和统计出来,再把点的个数算出来,乘上父亲分治中心到询问点距离即可。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#define LL long long
const int maxn=150007;
using namespace std;
int n,m,maxa,T,cnt,u,x,y,w;
LL ans;
int ls[maxn],a[maxn],b[maxn],f[maxn][20],dep[maxn],d[maxn];
struct edge{
int y,w,next;
}g[maxn*2];
struct rec{
int num,x;
LL y;
};
bool operator <(rec a,rec b)
{
return a.num<b.num;
}
struct line{
vector <rec> data[maxn];
void change(int p,int x,int k)
{
data[p].push_back((rec){x,1,k});
}
int find(int p,int x)
{
int l=0,r=data[p].size()-1,ans=-1;
while (l<=r)
{
int mid=(l+r)/2;
if (data[p][mid].num<=x) l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}
rec query(int p,int l,int r)
{
l=find(p,l-1),r=find(p,r);
if (r==-1) return (rec){0,0,0};
if (l==-1) return data[p][r];
return (rec){0,data[p][r].x-data[p][l].x,(LL)data[p][r].y-(LL)data[p][l].y};
}
}A,B;
void add(int x,int y,int w)
{
g[++cnt]=(edge){y,w,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int fa)
{
f[x][0]=fa;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
dep[y]=dep[x]+1;
d[y]=d[x]+g[i].w;
dfs(y,x);
}
}
int getlca(int x,int y)
{
if (dep[x]>dep[y]) swap(x,y);
int d=dep[y]-dep[x],k=19,t=1<<k;
while (d)
{
if (d>=t) d-=t,y=f[y][k];
t/=2,k--;
}
if (x==y) return x;
k=19;
while (k>=0)
{
if (f[x][k]!=f[y][k])
{
x=f[x][k];
y=f[y][k];
}
k--;
}
return f[x][0];
}
int getdis(int x,int y)
{
return d[x]+d[y]-2*d[getlca(x,y)];
}
struct tree{
int fa[maxn],f[maxn],size[maxn],vis[maxn],dis[maxn];
int root,sum;
queue <int> q;
void findroot(int x,int F)
{
size[x]=1;
f[x]=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((y==F) || (vis[y])) continue;
findroot(y,x);
size[x]+=size[y];
f[x]=max(f[x],size[y]);
}
f[x]=max(f[x],sum-size[x]);
if ((f[x]<f[root]) || (!root)) root=x;
}
void dfs(int x,int F)
{
size[x]=1;
A.change(root,a[x],dis[x]);
B.change(root,a[x],getdis(fa[root],x));
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((y==F) || (vis[y])) continue;
dis[y]=dis[x]+g[i].w;
dfs(y,x);
size[x]+=size[y];
if (x==root)
{
fa[y]=x;
q.push(y);
}
}
}
void build()
{
q.push(1);
size[1]=n;
while (!q.empty())
{
int x=q.front();
q.pop();
sum=size[x];
root=0;
findroot(x,0);
fa[root]=fa[x];
dis[root]=0;
dfs(root,0);
vis[root]=1;
sort(A.data[root].begin(),A.data[root].end());
sort(B.data[root].begin(),B.data[root].end());
for (int i=1;i<A.data[root].size();i++) A.data[root][i].x+=A.data[root][i-1].x;
for (int i=1;i<A.data[root].size();i++) A.data[root][i].y+=A.data[root][i-1].y;
for (int i=1;i<B.data[root].size();i++) B.data[root][i].x+=B.data[root][i-1].x;
for (int i=1;i<B.data[root].size();i++) B.data[root][i].y+=B.data[root][i-1].y;
}
}
LL getans(int x,int l,int r)
{
LL ans=0;
rec d=A.query(x,l,r);
ans+=d.y;
for (int i=x;fa[i];i=fa[i])
{
d=A.query(fa[i],l,r);
ans+=d.y+(LL)d.x*(LL)getdis(x,fa[i]);
d=B.query(i,l,r);
ans-=d.y+(LL)d.x*(LL)getdis(x,fa[i]);
}
return ans;
}
}Tree;
int main()
{
scanf("%d%d%d",&n,&T,&maxa);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
m=unique(b+1,b+n+1)-b-1;
for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
for (int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);
add(y,x,w);
}
dfs(1,0);
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
}
Tree.build();
for (int i=1;i<=T;i++)
{
LL L,R;
if (i==T)
{
L++;
}
scanf("%d%lld%lld",&u,&L,&R);
int l=min((LL)(L+ans)%(LL)maxa,(LL)(R+ans)%(LL)maxa);
int r=max((LL)(L+ans)%(LL)maxa,(LL)(R+ans)%(LL)maxa);
l=lower_bound(b+1,b+m+1,l)-b;
r=upper_bound(b+1,b+m+1,r)-b-1;
ans=Tree.getans(u,l,r);
printf("%lld\n",ans);
}
}