2226. 开店
题目链接
2226. 开店
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。
最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。
这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。
很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 \(n\) 个地方,编号为 \(1\) 到 \(n\),被 \(n-1\) 条带权的边连接起来。
每个地方都住着一个妖怪,其中第 \(i\) 个地方的妖怪年龄是 \(x_i\)。
妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。
所以这个树所有顶点的度数都小于或等于 \(3\)。
妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的 \(18\) 岁少女幽香和八云紫就比较喜欢可爱的东西。
幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方 \(u\)(\(u\)为编号),然后在 \(u\) 开一家面向年龄在 \(L\) 到 \(R\) 之间(即年龄大于等于 \(L\)、小于等于 \(R\))的妖怪的店。
也有可能 \(u\) 这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在 \(L\) 到 \(R\) 之间的妖怪,到点 \(u\) 的距离的和是多少(妖怪到 \(u\) 的距离是该妖怪所在地方到 \(u\) 的路径上的边的权之和),幽香把这个称为这个开店方案的方便值。
幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
输入格式
第一行三个用空格分开的数 \(n、Q\) 和 \(A\),表示树的大小、开店的方案个数和妖怪的年龄上限。
第二行 \(n\) 个用空格分开的数 \(x_1、x_2、…、x_n\),\(x_i\) 表示第 \(i\) 个地点妖怪的年龄,满足 \(0<=x_i<A\)。(年龄是可以为 \(0\) 的,例如刚出生的妖怪的年龄为 \(0\)。)
接下来 \(n-1\) 行,每行三个用空格分开的数 \(a、b、c\),表示树上的顶点 \(a\) 和 \(b\) 之间有一条权为 \(c\) 的边,\(a\) 和 \(b\) 是顶点编号。
接下来 \(Q\) 行,每行三个用空格分开的数 \(u、 a、 b\)。对于这 \(Q\) 行的每一行,用 \(a、b、A\) 计算出 \(L\) 和 \(R\),表示询问“在地方 \(u\) 开店,面向妖怪的年龄区间为 \([L,R]\) 的方案的方便值是多少”。
对于其中第 \(1\) 行,\(L\) 和 \(R\) 的计算方法为:\(L=\min(a\%A,b\%A)\), \(R=\max(a\%A,b\%A)\)。
对于第 \(2\) 到第 \(Q\) 行,假设前一行得到的方便值为 \(ans\),那么当前行的 \(L\) 和 \(R\) 计算方法为: \(L=\min((a+ans)\%A,(b+ans)\%A)\), \(R=\max((a+ans)\%A,(b+ans)\%A)\)。
输出格式
对于每个方案,输出一行表示方便值。
数据范围
\(1 \le n \le 150000\),
\(1 \le Q \le 200000\),
\(1 \le A \le 10^9\),
\(1 \le c \le 1000\)
输入样例:
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
输出样例:
1603
957
7161
9466
3232
5223
1879
1669
1282
0
解题思路
点分树,动态点分治
点分树就是每次点分治将重心与其余连通块的重心连接起来构建的树,将该树构建出来,存储需要的信息,可以很快处理一些询问
对于一次分治,如果询问点在重心上,则可以直接处理,否则需要点分治:递归+归并,归并包含其他子树上满足要求的点,答案分为两部分,一部分为询问点到重心的距离,另一部分为其他子树上满足要求的点到重心的距离,这部分可以通过预处理前缀和求得
难点主要是代码的编写
- 时间复杂度:\(O((m+n)\times log^2n)\)
代码
// Problem: 开店
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2228/
// Memory Limit: 256 MB
// Time Limit: 10000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1.5e5+5,inf=0x3f3f3f3f;
int n,m,A,age[N],res;
bool st[N];
vector<PII> adj[N];
struct father
{
int u,num;
LL dist;
};
vector<father> f[N];
struct Son
{
int age;
LL dist;
bool operator<(const Son &o)const
{
return age<o.age;
}
};
vector<Son> son[N][3];
int get_sz(int x,int fa)
{
if(st[x])return 0;
int res=1;
for(auto t:adj[x])
if(t.fi!=fa)res+=get_sz(t.fi,x);
return res;
}
int get_wc(int x,int fa,int sum,int &wc)
{
if(st[x])return 0;
int max_part=0,sz=1;
for(auto t:adj[x])
{
int y=t.fi;
if(y==fa)continue;
int y_sz=get_wc(y,x,sum,wc);
max_part=max(max_part,y_sz);
sz+=y_sz;
}
max_part=max(max_part,sum-sz);
if(res>max_part)wc=x,res=max_part;
return sz;
}
void get_dist(int x,int fa,LL dist,int wc,int k,vector<Son> &p)
{
if(st[x])return ;
p.pb({age[x],dist});
f[x].pb({wc,k,dist});
for(auto t:adj[x])
{
int y=t.fi,w=t.se;
if(y==fa)continue;
get_dist(y,x,dist+w,wc,k,p);
}
}
void cal(int x)
{
if(st[x])return ;
res=inf;
get_wc(x,0,get_sz(x,0),x);
st[x]=true;
int k=0;
for(auto t:adj[x])
{
int y=t.fi,w=t.se;
if(st[y])continue;
auto &p=son[x][k];
p.pb({-1,0}),p.pb({A+1,0});
get_dist(y,0,w,x,k,p);
sort(p.begin(),p.end());
for(int i=1;i<p.size();i++)p[i].dist+=p[i-1].dist;
k++;
}
for(auto t:adj[x])cal(t.fi);
}
LL query(int x,int l,int r)
{
LL res=0;
for(auto &t:f[x])
{
int u=t.u;
if(age[u]>=l&&age[u]<=r)res+=t.dist;
for(int i=0;i<3;i++)
{
auto &p=son[u][i];
if(i==t.num||p.empty())continue;
int a=lower_bound(p.begin(),p.end(),Son({l,-1}))-p.begin();
int b=upper_bound(p.begin(),p.end(),Son({r,-1}))-p.begin()-1;
res+=t.dist*(b-a+1)+p[b].dist-p[a-1].dist;
}
}
for(int i=0;i<3;i++)
{
auto &p=son[x][i];
if(p.empty())continue;
int a=lower_bound(p.begin(),p.end(),Son({l,-1}))-p.begin();
int b=upper_bound(p.begin(),p.end(),Son({r,-1}))-p.begin()-1;
res+=p[b].dist-p[a-1].dist;
}
return res;
}
int main()
{
cin>>n>>m>>A;
for(int i=1;i<=n;i++)cin>>age[i];
for(int i=1;i<n;i++)
{
int a,b,c;
cin>>a>>b>>c;
adj[a].pb({b,c}),adj[b].pb({a,c});
}
cal(1);
LL res=0;
while(m--)
{
int u,a,b;
cin>>u>>a>>b;
int l=(a+res)%A,r=(b+res)%A;
if(l>r)swap(l,r);
res=query(u,l,r);
cout<<res<<'\n';
}
return 0;
}