4012: [HNOI2015]开店
Time Limit: 70 Sec Memory Limit: 512 MBSubmit: 1850 Solved: 801
[ Submit][ Status][ Discuss]
Description
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到
人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。这样的
想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面
向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 n
个地方,编号为 1 到 n,被 n-1 条带权的边连接起来。每个地方都住着一个妖怪,
其中第 i 个地方的妖怪年龄是 x_i。妖怪都是些比较喜欢安静的家伙,所以它们并
不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 3。妖怪和人一
样,兴趣点随着年龄的变化自然就会变化,比如我们的 18 岁少女幽香和八云紫就
比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以
幽香打算选择一个地方 u(u为编号),然后在 u开一家面向年龄在 L到R 之间(即
年龄大于等于 L、小于等于 R)的妖怪的店。也有可能 u这个地方离这些妖怪比较
远,于是幽香就想要知道所有年龄在 L 到 R 之间的妖怪,到点 u 的距离的和是多
少(妖怪到 u 的距离是该妖怪所在地方到 u 的路径上的边的权之和) ,幽香把这个
称为这个开店方案的方便值。幽香她们还没有决定要把店开在哪里,八云紫倒是准
备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。
Input
第一行三个用空格分开的数 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(1 <= c <= 1000)的边,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)。
Output
对于每个方案,输出一行表示方便值。
Sample Input
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
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
Sample Output
1603
957
7161
9466
3232
5223
1879
1669
1282
0
957
7161
9466
3232
5223
1879
1669
1282
0
HINT
满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9
Source
题目大意:求所有权值范围在L~R之间的点到某个点的距离和啊,一道调了很久很久,代码重构了一遍的题目终于A掉了。
我真的太蒻了。。
说一下思路,分治树自然是必须的。然后就是每次维护子树的信息有子树在年龄x以前的所有点到子树根的距离和,还有年龄x以前到子树根父亲的距离和(为了消去对父亲的影响)。
至于具体的怎么做,建议大家先自己先尝试yy一下。
然后说一下我的做法,就是首先把每个子树所有的节点扔到一个vector里面,然后按年龄排个序。然后再更新到根和父亲的距离和。
查询的时候用lower_bound 和 upper_bound找到子树的两个年龄区间,区间减一下即可
这里有几个小技巧。首先,发现lower_bound之后由于是前缀和,所以要往前移一格。其次,upper_bound是大于的第一个,我们要的是大于等于的最后一个,所以也要往前移一格。这里lower_bound有可能溢出。所以干脆记录到根距离和的时候在vector前面多加一个0,这样还省得移来移去,还能防溢出。
然后还有,就是cmp的时候打的是age[a]<age[b],这个东东会在结构体里lower_bound里面把查询x变成age[x],所以在age里面多开一个数,然后age[iage]=x,这样在查询的时候就不会出问题。
哎,动态点分治常数真的好大好大,以后oi的时候还是多用树剖+主席树的为妙,动态点分治慎用!
剩下的就是一个码农的自我修养了,代码敬上。
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 155000;
const int inf = 0x7f7f7f7f;
int read()
{
char ch = getchar(); int x = 0, f = 1;
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int pre[maxn], top;
struct edge {
int to, next, w;
void add(int u, int v, int ww)
{
to = v; next = pre[u];
pre[u] = top; w = ww;
}
}e[maxn * 2];
void adds(int w, int v, int u) {
e[++top].add(u, v, w);
e[++top].add(v, u, w);
}
vector<int>ch[maxn];
vector<long long>fval[maxn], val[maxn];
int LOG[maxn * 2], bin[40];
int pos[maxn], son[maxn], f[maxn], prt[maxn], age[maxn];
bool vis[maxn];
int n, Q, A, tot, sums, G, iage;
long long dis[maxn], mn[maxn * 2][48], ans;
bool cmp(int a, int b) {return age[a] < age[b];}
void dfs(int u, int fa) {
mn[++tot][0] = dis[u]; pos[u] = tot;
for(int i = pre[u]; i; i = e[i].next)
if(e[i].to != fa) {
dis[e[i].to] = dis[u] + e[i].w;
dfs(e[i].to, u);
mn[++tot][0] = dis[u];
}
}
void RMQ_pre() {
for(int j = 1;j <= LOG[tot]; ++j)
for(int i = 1;i + bin[j] - 1 <= tot; ++i)
mn[i][j] = min(mn[i][j - 1], mn[i + bin[j - 1]][j - 1]);
}
long long rmq(int u, int v) {
u = pos[u]; v = pos[v];
if(u > v) swap(u, v);
int t = LOG[v - u + 1];
return min(mn[u][t], mn[v - bin[t] + 1][t]);
}
long long dist(int u, int v) {return dis[u] - (rmq(u, v) << 1) + dis[v];}
void G_get(int u, int fa) {
son[u] = 1; f[u] = 0;
for(int i = pre[u]; i; i = e[i].next)
if(e[i].to != fa && !vis[e[i].to]) {
G_get(e[i].to, u);
son[u] += son[e[i].to];
f[u] = max(f[u], son[e[i].to]);
}
f[u] = max(f[u], sums - son[u]);
if(f[G] > f[u]) G = u;
}
void Div(int u, int fa) {
prt[u] = fa; vis[u] = 1; int pre_sum = sums; ch[u].push_back(u);
for(int i = pre[u]; i; i = e[i].next)
if(!vis[e[i].to]) {
if(son[e[i].to] > son[u]) sums = pre_sum - son[u];
else sums = son[e[i].to];
G = 0; G_get(e[i].to, 0); int v = G;
Div(G, u);
for(int i = 0;i < ch[v].size(); ++i) ch[u].push_back(ch[v][i]);
}
sort(ch[u].begin(), ch[u].end(), cmp);
val[u].push_back(0);
for(int i = 1;i <= ch[u].size(); ++i)
val[u].push_back(dist(ch[u][i - 1], u) + val[u][i - 1]);
if(prt[u])
{
fval[u].push_back(0);
for(int i = 1;i <= ch[u].size(); ++i)
fval[u].push_back(dist(ch[u][i - 1], prt[u]) + fval[u][i - 1]);
}
}
void init() {
bin[0] = 1; for(int i = 1; i <= 30; ++i) bin[i] = bin[i - 1] << 1;
LOG[0] = -1; for(int i = 1; i <= 301000; ++i) LOG[i] = LOG[i >> 1] + 1;
n = read(); Q = read(); A = read();
for(int i = 1;i <= n; ++i) age[i] = read(); iage = n + 1;
for(int i = 1;i < n; ++i) adds(read(), read(), read());
dfs(1, 0); RMQ_pre();
G = 0; f[0] = inf; sums = n;
G_get(1, 0); Div(G, 0);
}
void query(int u, int st, int L, int R, int sonsize) {
age[iage] = L;int l = lower_bound(ch[u].begin(), ch[u].end(), iage, cmp) - ch[u].begin();
age[iage] = R;int r = upper_bound(ch[u].begin(), ch[u].end(), iage, cmp) - ch[u].begin();
ans += val[u][r] - val[u][l];
if(prt[u]) ans -= fval[u][r] - fval[u][l];
long long dt = dist(u, st);
ans += (r - l - sonsize) * dt;
if(prt[u]) query(prt[u], st, L, R, r - l);
}
void solve() {
while(Q--) {
int u = read(), a = (read() + ans) % A, b = (read() + ans) % A;
if(a > b) swap(a, b); ans = 0;
query(u, u, a, b, 0);
printf("%lld\n", ans);
}
}
int main()
{
init();
solve();
return 0;
}