\(\color{#0066ff}{ 题目描述 }\)

风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学。最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱。

这样的想法当然非常好啦,但是她们也发现她们面临着一个问题,那就是店开在哪里,面向什么样的人群。很神奇的是,幻想乡的地图是一个树形结构,幻想乡一共有 \(n\)个地方,编号为 \(1\) 到\(n\) 被\(n-1\) 条带权的边连接起来。每个地方都住着一个妖怪,其中第\(i\) 个地方的妖怪年龄是 \(x_i\) 。

妖怪都是些比较喜欢安静的家伙,所以它们并不希望和很多妖怪相邻。所以这个树所有顶点的度数都小于或等于 \(3\)。妖怪和人一样,兴趣点随着年龄的变化自然就会变化,比如我们的\(18\) 岁少女幽香和八云紫就比较喜欢可爱的东西。幽香通过研究发现,基本上妖怪的兴趣只跟年龄有关,所以幽香打算选择一个地方\(u\) (\(u\) 为编号),然后在\(u\)开一家面向年龄在\(L\) 到\(R\) 之间(即年龄大于等于\(L\) 小于等于\(R\) )的妖怪的店。

也有可能\(u\) 这个地方离这些妖怪比较远,于是幽香就想要知道所有年龄在\(L\) 到\(R\) 之间的妖怪,到点\(u\) 的距离的和是多少(妖怪到\(u\) 的距离是该妖怪所在地方到\(u\) 的路径上的边的权之和),幽香把这个称为这个开店方案的方便值。

幽香她们还没有决定要把店开在哪里,八云紫倒是准备了很多方案,于是幽香想要知道,对于每个方案,方便值是多少呢。

\(\color{#0066ff}{输入格式}\)

第一行三个用空格分开的数\(n,Q\) 和\(A\) ,表示树的大小、开店的方案个数和妖怪的年龄上限。

第二行\(n\) 个用空格分开的数\(x_1,x_2,\ldots,x_n;\)xi 表示第\(i\) 个地点妖怪的年龄,满足\(0\le x_i\lt A\) 。(年龄是可以为\(0\)的,例如刚出生的妖怪的年龄为\(0\) 。)

接下来\(n-1\) 行,每行三个用空格分开的数\(a\) 、\(b\) 、\(c\) ,表示树上的顶点\(a\) 和\(b\) 之间有一条权为\(c(1\le c\le1000)\)的边,\(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) 。

\(\color{#0066ff}{输出格式}\)

对于每个方案,输出一行表示方便值。

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

1603
957
7161
9466
3232
5223
1879
1669
1282
0

\(\color{#0066ff}{数据范围与提示}\)

满足\(n\le1.5*10^5,Q\le2*10^5\) 。对于所有数据,满足 \(A<=10^9\)

\(\color{#0066ff}{ 题解 }\)

动态点分治

建立点分树

每个点维护4个vector,一个是自己子树的age(有序加入),一个是对应的dis前缀和,我们考虑在age上二分找到L,R, 用这个下标在dis上收集ans

还有两个数组类似,记录对父亲的贡献

先在点分树让上把四个vector预处理出来,为了保证age有序,我们先把点按age从小到大排序,在更新

然后在点分树上统计贡献就行,注意卡二分边界

#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 2e5 + 10;
struct node {
int to;
LL dis;
node *nxt;
node(int to = 0, LL dis = 0, node *nxt = NULL): to(to), dis(dis), nxt(nxt) {}
void *operator new (size_t) {
static node *S = NULL, *T = NULL;
return (S == T) && (T = (S = new node[1024]) + 1024), S++;
}
};
LL n, Q, A;
using std::vector;
vector<LL> tofadis[maxn], tofaage[maxn], dis[maxn], age[maxn];
int siz[maxn], maxsiz[maxn], root, f[maxn][26], sum, rt, u[maxn], dep[maxn];
LL d[maxn][26], val[maxn];
node *head[maxn];
bool vis[maxn];
void add(int from, int to, LL dis) {
head[from] = new node(to, dis, head[from]);
}
void init() {
n = in(), Q = in(), A = in();
for(int i = 1; i <= n; i++) val[i] = in();
LL x, y, z;
for(int i = 1; i < n; i++) {
x = in(), y = in(), z = in();
add(x, y, z), add(y, x, z);
}
}
void getroot(int x, int fa) {
siz[x] = 1;
maxsiz[x] = 0;
for(node *i = head[x]; i; i = i->nxt) {
if(i->to == fa || vis[i->to]) continue;
getroot(i->to, x);
siz[x] += siz[i->to];
maxsiz[x] = std::max(maxsiz[x], siz[i->to]);
}
maxsiz[x] = std::max(maxsiz[x], sum - siz[x]);
if(maxsiz[x] < maxsiz[root]) root = x;
}
void build(int x) {
vis[x] = true;
for(node *i = head[x]; i; i = i->nxt) {
if(vis[i->to]) continue;
root = 0, sum = siz[i->to];
getroot(i->to, 0);
u[root] = x;
build(root);
}
}
void build() {
maxsiz[0] = sum = n;
getroot(1, 0);
rt = root;
build(root);
}
void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
f[x][0] = fa;
for(node *i = head[x]; i; i = i->nxt) {
if(i->to == fa) continue;
dfs(i->to, x);
d[i->to][0] = i->dis;
}
}
void beizeng() {
dfs(1, 0);
for(int j = 1; j <= 24; j++)
for(int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j - 1]][j - 1];
d[i][j] = d[f[i][j - 1]][j - 1] + d[i][j - 1];
}
}
LL LCA(int x, int y) {
LL D = 0;
if(dep[x] < dep[y]) std::swap(x, y);
for(int i = 24; i >= 0; i--) if(dep[f[x][i]] >= dep[y]) D += d[x][i], x = f[x][i];
if(x == y) return D;
for(int i = 24; i >= 0; i--) if(f[x][i] != f[y][i]) D += d[x][i] + d[y][i], x = f[x][i], y = f[y][i];
return D + d[x][0] + d[y][0];
} bool cmp(const int &a, const int &b) { return val[a] < val[b]; }
void predoit() {
static int id[maxn];
for(int i = 1; i <= n; i++) id[i] = i;
std::sort(id + 1, id + n + 1, cmp);
for(int i = 1; i <= n; i++) {
int now = id[i];
//当前点的年龄,dis前缀和
age[now].push_back(val[now]);
dis[now].push_back(dis[now].empty()? 0 : dis[now].back());
for(int o = now; u[o]; o = u[o]) {
LL D = LCA(now, u[o]);
//当前点父亲的年龄,dis前缀和
age[u[o]].push_back(val[now]);
dis[u[o]].push_back(dis[u[o]].empty()? D : dis[u[o]].back() + D);
//当前点对父亲的贡献,同样维护
tofaage[o].push_back(val[now]);
tofadis[o].push_back(tofadis[o].empty()? D : tofadis[o].back() + D);
}
}
} LL calc(int pos, LL L, LL R) {
LL posl, posr;
LL ans;
//初始为pos子树自己的贡献
posl = std::lower_bound(age[pos].begin(), age[pos].end(), L) - age[pos].begin() - 1;
posr = std::upper_bound(age[pos].begin(), age[pos].end(), R) - age[pos].begin() - 1;
//注意卡边界
if(R < age[pos].front() || L > age[pos].back()) ans = 0;
else if(posl == -1) ans = dis[pos][posr];
else ans = dis[pos][posr] - dis[pos][posl];
for(int o = pos; u[o]; o = u[o]) {
LL tot, fal, far;
//统计ans在age里二分,在dis里收集答案
posl = std::lower_bound(tofaage[o].begin(), tofaage[o].end(), L) - tofaage[o].begin() - 1;
posr = std::upper_bound(tofaage[o].begin(), tofaage[o].end(), R) - tofaage[o].begin() - 1; if(R < tofaage[o].front() || L > tofaage[o].back()) tot = 0;
else if(posl == -1) tot = tofadis[o][posr];
else tot = tofadis[o][posr] - tofadis[o][posl];
//这个减去的是自己对父亲的贡献,现在要统计父亲的其他子树的贡献,自己的贡献会重(父亲子树的贡献-自己子树的贡献=兄弟子树的贡献(对父亲的贡献))
ans -= tot; LL D = LCA(u[o], pos);
fal = std::lower_bound(age[u[o]].begin(), age[u[o]].end(), L) - age[u[o]].begin() - 1;
far = std::upper_bound(age[u[o]].begin(), age[u[o]].end(), R) - age[u[o]].begin() - 1; if(R < age[u[o]].front() || L > age[u[o]].back()) tot = 0;
else if(fal == -1) tot = dis[u[o]][far];
else tot = dis[u[o]][far] - dis[u[o]][fal];
//父亲子树到父亲的贡献,因为上面已经减去了那部分,所以不会重复
ans += tot;
//上面的是父亲其他子树到父亲的贡献,这是其中一段距离,我们的目的是他们到pos的距离,所以还差父亲到pos的距离
ans += ((far - fal) - (posr - posl)) * D;
}
return ans;
}
void query() {
LL pos, a, b, L, R, ans = 0;
while(Q --> 0) {
pos = in(), a = in(), b = in();
L = std::min((a + ans) % A, (b + ans) % A), R = std::max((a + ans) % A, (b + ans) % A);
ans = calc(pos, L, R);
printf("%lld\n", ans);
}
}
int main() {
init();
build();
beizeng();
predoit();
query();
return 0;
}

P3241 [HNOI2015]开店 动态点分治的更多相关文章

  1. [BZOJ4012][HNOI2015]开店(动态点分治,树链剖分)

    4012: [HNOI2015]开店 Time Limit: 70 Sec  Memory Limit: 512 MBSubmit: 2168  Solved: 947[Submit][Status] ...

  2. 【bzoj4012】[HNOI2015]开店 动态点分治+STL-vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的想法当然非常好啦,但是她们也发现她们面临着一个问题 ...

  3. 【BZOJ4012】[HNOI2015]开店 动态树分治+二分

    [BZOJ4012][HNOI2015]开店 Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点 ...

  4. luogu 3241 [HNOI2015]开店 动态点分治+二分+vector

    独立写出来+想出来的,1.5h就切了~ 建立点分树,然后用 $vector$ 暴力存所有子节点,然后二分一下子就可以了. #include <cstdio> #include <ve ...

  5. 洛谷 P3241 [HNOI2015]开店 解题报告

    P3241 [HNOI2015]开店 题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱. 这样的想法当然非 ...

  6. [loj2116]「HNOI2015」开店 动态点分治

    4012: [HNOI2015]开店 Time Limit: 70 Sec  Memory Limit: 512 MBSubmit: 2452  Solved: 1089[Submit][Status ...

  7. P3241 [HNOI2015]开店

    题解:动态点分治 建立点分树 每个点维护点分树子树内节点到这个节点和父亲节点距离的前缀和 二分查找锁定合法区间 对每个祖先分治中心查询路径和然后减去不合法子树内的路径和 注意:求大量LCA时用树剖 不 ...

  8. LOJ2116 [HNOI2015] 开店 【点分治】

    题目分析: 观察题目发现度数不小于三,考虑从边分治入手,用点分治代替.将树划分成重心链接的结构,称为点分树.令当前询问的点为$ u $.那么我们考虑点分树的根到$ u $的一条路径.考虑根结点,排除掉 ...

  9. 并不对劲的bzoj4012:loj2116:p3241: [HNOI2015]开店

    题目大意 有一棵\(n\)(\(n\leq1.5*10^5\))个节点的二叉树,有点权\(x\),边权\(w\),\(q\)(\(q\leq2*10^5\))组询问,每组询问给出\(u,l,r\),求 ...

随机推荐

  1. postgresql 数据库,模式,表空间的关系

    数据库与模式模式(schema)是对数据库(database)逻辑分割在数据库创建的同时,就已经默认为数据库创建了一个模式--public,这也是该数据库的默认模式.所有为此数据库创建的对象(表.函数 ...

  2. vmstat详细说明

    下面是关于Unix下vmstat命令的详细介绍,收录在这里,以备日后参考 vmstat是用来实时查看内存使用情况,反映的情况比用top直观一些.作为一个CPU监视器,vmstat命令比iostat命令 ...

  3. 基于C++任意点数的FFT/IFFT(时域和频域)实现

    函数说明:更改主函数体中的N和length(=log2(N))既可以实现任意点数(2的幂次)的FFT/ IFFT的实现,fft函数中flag标志位控制是正变换还是逆变换. 1.复数操作类      定 ...

  4. 浅析大数据的技术生态圈(Hadoop,hive,spark)

    大数据本身是个很宽泛的概念,Hadoop生态圈(或者泛生态圈)基本上都是为了处理超过单机尺度的数据处理而诞生的.你可以把它比作一个厨房所以需要的各种工具.锅碗瓢盆,各有各的用处,互相之间又有重合.你可 ...

  5. C++面向对象类的实例题目六

    问题描述: 编写一个程序计算两个给定长方形的面积,其中在设计类成员函数addarea()(用于计算两个长方形的总面积)时使用对象作为参数. 程序代码: #include<iostream> ...

  6. js 控制标记样式

    做一个变色的标签 鼠标移入变为灰色,移除变回原来的颜色,点击变成黑色,再点击变回,如果变成黑色不受移入移除影响. <body> <div class="bt1" ...

  7. GCC 版本与C11标准

    1. GCC版本是否支持C11 C89=C90:gcc选项是:-ansi, -std=c90 or -std=iso9899:; 带了GNU扩展的:-std=gnu90 C94=C95:gcc选项:- ...

  8. wpf textblock超出显示范围后显示tooltip

    public static class TextTrmmingShowToolTip { public static readonly DependencyProperty IsToolTipProp ...

  9. Linux tput命令

    一.简介 shell 脚本编写者往往需要能通过一种方法将输出更改为粗体,为其加下划线,实现反向突出显示等,这正是 tput 的用武之地. tput 命令将通过 terminfo 数据库对您的终端会话进 ...

  10. 华为2013年西安java机试题目:如何过滤掉数组中的非法字符。

    这道题目为记忆版本: 题目2描述: 编写一个算法,过滤掉数组中的非法字符,最终只剩下正式字符. 示例:输入数组:“!¥@&HuaWei*&%123” 调用函数后的输出结果,数组:“Hu ...