[HNOI 2015]开店
Description
风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到
Input
第一行三个用空格分开的数 n、Q和A,表示树的大小、开店的方案个数和妖
Output
对于每个方案,输出一行表示方便值。
Sample Input
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
957
7161
9466
3232
5223
1879
1669
1282
0
HINT
满足 n<=150000,Q<=200000。对于所有数据,满足 A<=10^9
题解
若 $u,v$ 在一棵树上,显然存在 $$dist_{u, v} = dist_{root, u}+dist_{root, v}-2*dist_{root, lca(u, v)}$$
其中 $dist_{u, v}$ 表示树上 $u<->v$ 之间的路径长度。
而对于此题,显然每组询问 $[L, R]$ 的答案,就是(记点权在集合 $[L, R]$ 中的点为点集 $S$ ):
$$ans = \sum_{v \in S} (dist_{root, u}+dist_{root, v}-2*dist_{root, lca(u, v)})$$
$$= |S|*dist_{root, u}+\sum_{v \in S}dist_{root, v}-2*\sum_{v \in S}dist_{root, lca(u, v)}$$
注意到 $|S|*dist_{root, u}$ 可以直接求;我们按每个点的点权排序,显然 $\sum_{v \in S}dist_{root, v}$ 是可以用前缀和预处理出来的。
现在需要解决的问题就是如何求 $\sum_{v \in S}dist_{root, lca(u, v)}$ 。
我们注意到这样一个事情:对于求 $dist_{root, lca(u, v)}$ ,我们可以先将 $root<->u$ 的路径上的边标记;再询问 $root<->v$ 的边,询问到的有标记边的就是 $root<->lca(u, v)$ 上的边。
对于这个方法,我们可以将按点权排序后的点,按序存到主席树中。询问 $[L, R]$ 就是询问 $[1, R]-[1, L-1]$ 。
实现方面,值得注意的是,对于新建的主席树,因为树剖每次会修改可能不止一个区间,所以在这个版本下新建的节点可以修改,我们记录一个数组 $pre_i$ 表示第 $i$ 个版本(及之前)动态开点开的最大的值。新建节点时,若当前节点 $o > pre$ 即 $o$ 是当前版本才新建的,所以可以直接修改。
询问的时候,若有 $lazy$ 标记,我是直接暴力下放的,不知道有没有更好的方法。
//It is made by Awson on 2018.1.1
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = ;
const int M = N*;
const int INF = ~0u>>; int n, q, a, u, v, c, x1, x2;
struct tt {
int to, next, cost;
}edge[(N<<)+];
int path[N+], tot;
struct value {
int x, id;
bool operator < (const value &b) const {
return x < b.x;
}
}w[N+];
int size[N+], son[N+], sonc[N+], fa[N+], dep[N+], pos[N+], top[N+], cnt;
LL val[N+], dist[N+], sumdist[N+];
struct Segment_tree {
int root[N+], pre[N+], lazy[M+], ch[M+][], pos;
LL sum[M+];
void pushdown(int o, int l, int mid, int r, int last) {
if (ch[o][] <= last) {
int ls = ch[o][]; ch[o][] = ++pos;
lazy[ch[o][]] = lazy[ls], ch[ch[o][]][] = ch[ls][], ch[ch[o][]][] = ch[ls][], sum[ch[o][]] = sum[ls];
}
if (ch[o][] <= last) {
int rs = ch[o][]; ch[o][] = ++pos;
lazy[ch[o][]] = lazy[rs], ch[ch[o][]][] = ch[rs][], ch[ch[o][]][] = ch[rs][], sum[ch[o][]] = sum[rs];
}
sum[ch[o][]] += (val[mid]-val[l-])*lazy[o], sum[ch[o][]] += (val[r]-val[mid])*lazy[o];
lazy[ch[o][]] += lazy[o], lazy[ch[o][]] += lazy[o], lazy[o] = ;
}
void update(int &o, int l, int r, int a, int b, int last) {
if (o <= last) {
int rt = o; o = ++pos;
lazy[o] = lazy[rt], ch[o][] = ch[rt][], ch[o][] = ch[rt][], sum[o] = sum[rt];
}
if (a <= l && r <= b) {
lazy[o] += , sum[o] += val[r]-val[l-];
return;
}
int mid = (l+r)>>;
if (lazy[o]) pushdown(o, l, mid, r, last);
if (mid >= a) update(ch[o][], l, mid, a, b, last);
if (mid < b) update(ch[o][], mid+, r, a, b, last);
sum[o] = sum[ch[o][]]+sum[ch[o][]];
}
LL query(int o, int l, int r, int a, int b) {
if (a <= l && r <= b) return sum[o];
int mid = (l+r)>>; LL c1 = , c2 = ;
if (lazy[o]) pushdown(o, l, mid, r, INF);
if (mid >= a) c1 = query(ch[o][], l, mid, a, b);
if (mid < b) c2 = query(ch[o][], mid+, r, a, b);
return c1+c2;
}
}T; void add(int u, int v, int c) {
edge[++tot].to = v;
edge[tot].cost = c;
edge[tot].next = path[u];
path[u] = tot;
}
void dfs1(int u, int father, int depth) {
size[u] = , fa[u] = father, dep[u] = depth;
for (int i = path[u]; i; i = edge[i].next)
if (edge[i].to != father) {
dfs1(edge[i].to, u, depth+);
size[u] += size[edge[i].to];
if (size[edge[i].to] > size[son[u]]) son[u] = edge[i].to, sonc[u] = edge[i].cost;
}
}
void dfs2(int u, int tp, int cost) {
top[u] = tp, pos[u] = ++cnt, val[cnt] = cost;
if (son[u]) {
dist[son[u]] = dist[u]+sonc[u];
dfs2(son[u], tp, sonc[u]);
}
for (int i = path[u]; i; i = edge[i].next)
if (edge[i].to != fa[u] && edge[i].to != son[u]) {
dist[edge[i].to] = dist[u]+edge[i].cost;
dfs2(edge[i].to, edge[i].to, edge[i].cost);
}
}
void lca_update(int &o, int x, int last) {
while (x) {
T.update(o, , n, pos[top[x]], pos[x], last);
x = fa[top[x]];
}
}
LL lca_query(int o, int x) {
LL ans = ;
while (x) {
ans += T.query(o, , n, pos[top[x]], pos[x]);
x = fa[top[x]];
}
return ans;
}
int lowerbound(int x) {
int L = , R = n, ans = ;
while (L <= R) {
int mid = (L+R)>>;
if (w[mid].x < x) L = mid+;
else R = mid-, ans = mid;
}
return ans;
}
int upperbound(int x) {
int L = , R = n, ans = ;
while (L <= R) {
int mid = (L+R)>>;
if (w[mid].x > x) R = mid-;
else L = mid+, ans = mid;
}
return ans;
}
void work() {
scanf("%d%d%d", &n, &q, &a);
for (int i = ; i <= n; i++) scanf("%d", &w[i].x), w[i].id = i;
sort(w+, w++n);
for (int i = ; i < n; i++) {
scanf("%d%d%d", &u, &v, &c);
add(u, v, c); add(v, u, c);
}
dfs1(, , ), dfs2(, , );
for (int i = ; i <= n; i++) val[i] += val[i-];
for (int i = ; i <= n; i++) {
T.root[i] = T.root[i-];
lca_update(T.root[i], w[i].id, T.pre[i-]);
T.pre[i] = T.pos, sumdist[i] = sumdist[i-]+dist[w[i].id];
}
LL ans = ;
while (q--) {
scanf("%d%d%d", &u, &x1, &x2);
x1 = (x1+ans)%a, x2 = (x2+ans)%a;
if (x1 > x2) swap(x1, x2);
x1 = lowerbound(x1), x2 = upperbound(x2);
LL a1 = lca_query(T.root[x1-], u), a2 = lca_query(T.root[x2], u);
ans = (x2-x1+)*dist[u]+sumdist[x2]-sumdist[x1-]-(a2-a1)*;
printf("%lld\n", ans);
}
}
int main() {
work();
return ;
}
主席树
upd 18.1.8:拿动态点分玩♂下这道题。
网上题解看不懂,自己 YY 了一个做 Fa♂。似乎和题解是一样的...
首先建成点分树后,我们用一个数据结构来维护一下所有点到重心的距离,记点分树上 $o$ 的父亲为 $fa_o$ ,并维护所有点到 $fa_o$ 的距离。
我们可以用 $vector$ 来存下这些数据。对于查询 $[L, R]$ 我们可以二分 $vector$ 。所以我们先不讨论范围问题。
查询时,我们发现,在点分树上爬的时候,我们需要统计除了来的子树外的其他子树中所有节点到当前重心的距离,并且加上这一部分通过重心到询问的点的距离。
如何求这一部分通过重心到询问的点的距离?我们可以在处理上个重心的时候预先减去 $size_x*dist(o, fa_x)$ ,再在处理这个重心时加上 $size_x*dist(x, o)$ 。注意 $x$ 是当前处理的重心,即上述两式中 $x$ 的含义不一样。
如何统计除了来的子树外的其他子树中所有节点到当前重心的距离?也用上面的思想,我们可以先减去以上一个重心为根的子树中所有点到当前处理的的重心的距离和,然后再直接加上以当前处理的重心为根的子树中所有的点到重心的距离。
总时间复杂度就是 $O((n+q)log_2 ^2 n)$ 。
//It is made by Awson on 2018.1.8
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define RE register
#define lowbit(x) ((x)&(-(x)))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
using namespace std;
const int N = ;
const int INF = ~0u>>;
void read(int &x) {
char ch; bool flag = ;
for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || ); ch = getchar());
for (x = ; isdigit(ch); x = (x<<)+(x<<)+ch-, ch = getchar());
x *= -*flag;
} int n, q, A, col[N+], a, b, c;
struct tt {int to, next, cost; }edge[(N<<)+];
int path[N+], top;
struct arr {
LL x, xlca; int id;
arr() {}
arr(LL _x, LL _xlca, int _id) {x = _x, xlca = _xlca, id = _id; }
bool operator < (const arr &b) const {return id < b.id; }
};
vector<arr>d[N+];
int fa[N+];
LL ans;
void add(int u, int v, int c) {
edge[++top].to = v;
edge[top].next = path[u];
edge[top].cost = c;
path[u] = top;
}
namespace LCA {
int bin[], lim, dep[N+], fa[N+][];
LL dis[N+];
void dfs(int o, int depth, int father, LL dist) {
dis[o] = dist;
fa[o][] = father, dep[o] = depth;
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != father) dfs(edge[i].to, depth+, o, dist+edge[i].cost);
}
int query(int x, int y) {
if (dep[x] < dep[y]) Swap(x, y);
for (int i = lim; i >= ; i--) if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if (x == y) return x;
for (int i = lim; i >= ; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][];
}
LL dist(int x, int y) {return dis[x]+dis[y]-(dis[query(x, y)]<<); }
void main() {
lim = log(n)/log(), bin[] = ; for (int i = ; i <= ; i++) bin[i] = bin[i-]<<;
dfs(, , , );
for (int t = ; t <= lim; t++) for (int i = ; i <= n; i++) fa[i][t] = fa[fa[i][t-]][t-];
}
}
namespace Point_divide {
int size[N+], mx[N+], vis[N+], minsize, root;
void get_size(int o, int fa) {
size[o] = , mx[o] = ;
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != fa && !vis[edge[i].to]) {
get_size(edge[i].to, o);
size[o] += size[edge[i].to];
if (size[edge[i].to] > mx[o]) mx[o] = size[edge[i].to];
}
}
void get_root(int o, int pa, int fa) {
mx[o] = Max(mx[o], size[pa]-size[o]);
if (mx[o] < minsize) minsize = mx[o], root = o;
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, pa, o);
}
void get_dist(int o, int rt, int pa, int fa, int dist) {
d[rt].push_back(arr(dist, LCA::dist(pa, o), col[o]));
for (int i = path[o]; i; i = edge[i].next)
if (edge[i].to != fa && !vis[edge[i].to]) get_dist(edge[i].to, rt, pa, o, dist+edge[i].cost);
}
void solve(int o, int father) {
minsize = INF; get_size(o, ), get_root(o, o, );
vis[root] = , fa[root] = father; d[root].push_back(arr(, LCA::dist(root, father), col[root])); int rt = root;
for (int i = path[root]; i; i = edge[i].next)
if (!vis[edge[i].to]) get_dist(edge[i].to, root, father, root, edge[i].cost);
d[root].push_back(arr(, , -));
sort(d[root].begin(), d[root].end()); int size = d[root].size();
for (int i = ; i < size; i++) d[root][i].x += d[root][i-].x, d[root][i].xlca += d[root][i-].xlca;
for (int i = path[root]; i; i = edge[i].next)
if (!vis[edge[i].to]) solve(edge[i].to, rt);
}
void main() {solve(, ); }
}
int upperbound(int o, int key) {
int L = , R = d[o].size()-, mid, ans = R;
while (L <= R) {
mid = (L+R)>>;
if (d[o][mid].id <= key) L = mid+, ans = mid;
else R = mid-;
}
return ans;
}
int lowerbound(int o, int key) {
int L = , R = d[o].size()-, mid, ans = ;
while (L <= R) {
mid = (L+R)>>;
if (d[o][mid].id <= key) L = mid+, ans = mid;
else R = mid-;
}
return ans;
}
void query(int o, int l, int r) {
for (int x = o, L, R; x; x = fa[x]) {
R = upperbound(x, r), L = lowerbound(x, l-);
ans += (R-L)*LCA::dist(x, o);
if (fa[x]) ans -= d[x][R].xlca-d[x][L].xlca+(LL)(R-L)*LCA::dist(fa[x], o);
ans += d[x][R].x-d[x][L].x;
}
}
void work() {
read(n), read(q), read(A);
for (int i = ; i <= n; i++) read(col[i]);
for (int i = ; i < n; i++) read(a), read(b), read(c), add(a, b, c), add(b, a, c);
LCA::main(); Point_divide::main();
while (q--) {
read(c), read(a), read(b); a = (ans+a)%A, b = (ans+b)%A;
if (a > b) Swap(a, b); ans = ;
query(c, a, b); printf("%lld\n", ans);
}
}
int main() {
work();
return ;
}
动态点分
[HNOI 2015]开店的更多相关文章
- 解题:HNOI 2015 开店
题面 根据树上距离的计算方法,可以先把答案化成$\sum dep_i+n*dep_u-\sum 2*dep[LCA(i,u)]$的形式,然后维护$\sum 2*dep[LCA(i,u)]$ 把妖怪们按 ...
- [HNOI 2015]实验比较
Description 小D 被邀请到实验室,做一个跟图片质量评价相关的主观实验.实验用到的图片集一共有 N 张图片,编号为 1 到 N.实验分若干轮进行,在每轮实验中,小 D会被要求观看某两张随机选 ...
- [HNOI 2015]亚瑟王
Description 小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑. 他决定,在脱坑之前,最后再来打一盘亚瑟王.既然是最后一战,就一定要打得漂 亮.众所周知,亚瑟王是一 ...
- [HNOI 2015]接水果
Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果. 由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更 ...
- [HNOI 2015]菜肴制作
Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...
- [HNOI 2015]落忆枫音
Description 「恒逸,你相信灵魂的存在吗?」 郭恒逸和姚枫茜漫步在枫音乡的街道上.望着漫天飞舞的红枫,枫茜突然问出 这样一个问题. 「相信吧.不然我们是什么,一团肉吗?要不是有灵魂……我们 ...
- 【题解】亚瑟王 HNOI 2015 BZOJ 4008 概率 期望 动态规划
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4008 一道不简单的概率和期望dp题 根据期望的线性性质,容易想到,可以算出每张卡的期望伤害, ...
- 「HNOI 2015」实验比较
\(Description\) 有\(n\)个元素,对于每个元素\(x_i\)最多知道一个形如\(x_j < x_i\)或\(x_j=x_i\)的条件,问有多少合法的序列.合法的序列满足每个元素 ...
- [BZOJ 4010][HNOI 2015] 菜肴制作
4010: [HNOI2015]菜肴制作 Time Limit: 5 Sec Memory Limit: 512 MBSubmit: 1776 Solved: 889[Submit][Status ...
随机推荐
- java编程思想笔记(1)
java编程思想笔记(1) 一,对象的创建和生命周期 对象的数据位于何处?怎样控制对象的生命周期? 在堆(heap)的内存池中动态地创建对象. java完全采用了动态内存分配方式. 二,垃圾回收器 自 ...
- JavaScript(第五天)【流程控制语句】
ECMA-262规定了一组流程控制语句.语句定义了ECMAScript中的主要语法,语句通常由一个或者多个关键字来完成给定的任务.诸如:判断.循环.退出等. 一.语句的定义 在ECMAScri ...
- 用C语言协助办公_01 找出所有不对劲的人
近期想出一系列用C语言协助办公的视频教程,这是第一个.具体的移步:https://chuanke.baidu.com/v6658388-240377-1789288.html
- 第2次作业:Wechat创作史
Wechat创作史 比尔盖茨曾经说过一句话:21世纪要么电子商务,要么无商可务. 2.1 介绍产品相关信息 -the information about Wechat 你选择的产品是? 选择微信作 ...
- Beta冲刺置顶随笔
项目名称:城市安全风险管控系统 小组成员: 张梨贤.林静.周静平.黄腾飞 Beta冲刺随笔 Beta预备 Beta冲刺Day1 Beta冲刺Day2 Beta冲刺Day3 Beta冲刺Day4 Bet ...
- mongodb 高级操作
聚合 aggregate 聚合(aggregate)主要用于计算数据,类似sql中的sum().avg() 语法 db.集合名称.aggregate([{管道:{表达式}}]) 管道 管道在Unix和 ...
- python 进程复习
import os import time ret = os.fork() # 创建子线程 if ret ==0: # 子进程中返回值为0,父进程>0 while True: print('.. ...
- scrapy crawl xmlfeed spider
from scrapy.spiders import XMLFeedSpider from myxml.items import MyxmlItem class XmlspiderSpider(XML ...
- JAVA_SE基础——58.如何用jar命令对java工程进行打包
有时候为了更方便快捷的部署和执行Java程序,要把java应用程序打包成一个jar包.而这个基础的操作有时候也很麻烦,为了方便java程序员们能够方便的打包java应用程序,下面对jar命令进行介绍, ...
- 【ASP.NET Core】依赖注入高级玩法——如何注入多个服务实现类
依赖注入在 ASP.NET Core 中起中很重要的作用,也是一种高大上的编程思想,它的总体原则就是:俺要啥,你就给俺送啥过来.服务类型的实例转由容器自动管理,无需我们在代码中显式处理. 因此,有了依 ...