洛谷题目链接:[HNOI2015]接水果

题目描述

风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果。由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本。

首先有一个地图,是一棵由 n 个顶点、n-1 条边组成的树(例如图 1给出的树包含 8 个顶点、7 条边)。

这颗树上有 P 个盘子,每个盘子实际上是一条路径(例如图 1 中顶点 6 到顶点 8 的路径),并且每个盘子还有一个权值。第 i 个盘子就是顶点a_i到顶点b_i的路径(由于是树,所以从a_i到b_i的路径是唯一的),权值为c_i。

接下来依次会有Q个水果掉下来,每个水果本质上也是一条路径,第i 个水果是从顶点 u_i 到顶点v_i 的路径。

幽香每次需要选择一个盘子去接当前的水果:一个盘子能接住一个水果,当且仅当盘子的路径是水果的路径的子路径(例如图1中从 3到7 的路径是从1到8的路径的子路径)。这里规定:从a 到b的路径与从b到 a的路径是同一条路径。

当然为了提高难度,对于第 i 个水果,你需要选择能接住它的所有盘子中,权值第 k_i 小的那个盘子,每个盘子可重复使用(没有使用次数的上限:一个盘子接完一个水果后,后面还可继续接其他水果,只要它是水果路径的子路径)。幽香认为这个游戏很难,你能轻松解决给她看吗?

输入输出格式

输入格式:

第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数。 接下来n-1 行,每行两个数 a、b,表示树上的a和b 之间有一条边。树中顶点按1到 n标号。 接下来 P 行,每行三个数 a、b、c,表示路径为 a 到 b、权值为 c 的盘子,其中0<=c<=10^9,a不等于b。 接下来Q行,每行三个数 u、v、k,表示路径为 u到 v的水果,其中 u不等于v,你需要选择第 k小的盘子,第k 小一定存在。

输出格式:

对于每个果子,输出一行表示选择的盘子的权值。

输入输出样例

输入样例#1:

10 10 10
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
3 2 217394434
10 7 13022269
6 7 283254485
6 8 333042360
4 6 442139372
8 3 225045590
10 4 922205209
10 8 808296330
9 2 486331361
4 9 551176338
1 8 5
3 8 3
3 8 4
1 8 3
4 8 1
2 3 1
2 3 1
2 3 1
2 4 1
1 4 1

输出样例#1:

442139372
333042360
442139372
283254485
283254485
217394434
217394434
217394434
217394434
217394434

说明

N,P,Q<=40000。

一句话题意:
给你一个树上路径集合\(S\),每条路径有个权值.每次询问一条路径\(p:x\to y\),问他在\(S\)中包含的路径中权值第\(k\)小的是多少.

题解: 我们首先来考虑如何确定路径的包含关系.首先我们需要现将这颗树剖分一下,标记每个点的\(dfs\)序,用\(L[x]\)表示\(x\)的\(dfs\)序,\(R[x]\)表示\(L[x]+size[x]-1\).

然后我们可以将一条树上的路径\((u,v)\)看作是一个二元组\((u,v)\),将这个二元组的\(dfs\)序映射到二维平面上,也就是一个点\(L[u],L[v]\).这样我们就可以比较方便的表示出一条树上路径.

接着我们分类讨论一下.假设路径\((u,v)\)是\((x,y)\)的子路径,且\(deep[u]<deep[v]\),则有:

  1. 若\(lca(u,v)==u\)

    设\(z\)是\(u\to v\)上的第一个点
    那么就要求路径\(p\)满足一个节点在\(v\)子树内,一个节点在\(z\)子树外
    也就是说这次修改操作可以影响到的范围是\(\{(1,L[z]-1),(L[v],R[v])\}\)和\(\{(R[z]+1),(L[v],R[v])\}\)
    而这个影响的范围正好对应了二维平面上的一个矩形.
    所以对于查询的一条路径可以直接在二维平面上单点查询.

  2. 若\(lca(u,v)!=u\)

    则这次修改可以影响到的范围是\(\{(L[u],R[u]),(L[v],R[v])\}\),同样是一个矩形范围.

经过上面的分析,我们发现,要统计修改所造成的贡献,只需要统计一个二维前缀和就可以了,所以可以采用树状数组来修改查询.

然后我们会发现,如果二维修改复杂度太大了,过不了.所以这时候我们需要使用扫描线 优化一下这个矩阵修改的过程,也就是将一个矩形的修改变成两条线段的修改,这样复杂度就降低了一个维度.

最后我们来考虑如何回答询问.因为修改操作对询问都可以产生贡献,而每个修改都是独立的,并且又要求我们求出一个询问的第\(k\)小,所以我们可以采用整体二分.

我们先将所有修改操作改成一根根的扫描线,然后将修改操作的扫描线按照\(x\)轴顺序排个序.查询操作也需要按照\(x\)轴坐标排个序.因为我这里是直接将扫描线加入了整体二分的过程,所以要保证在处理询问的时候只有比当前询问\(x\)轴坐标小的对这次询问作贡献.

然后在整体二分的过程中枚举当前区间中所有比当前查询的\(x\)轴坐标小的修改操作加入树状数组中,因为已经将\(x\)轴进行了排序,所以在树状数组中只需要查询\(y\)轴的坐标(排序保证了当前查询状态是最新的).

然后有一个要注意的重要的细节:因为对于一次修改操作,修改的这条路径是无向的.所以修改的矩形可以算成两个,这时候如果只修改一个就涉及到了\(x\)轴\(y\)轴具体要修改哪一个的问题.比如说修改一个区间\(\{(L[u],R[u]),(L[v],R[v])\}\),既可以是前面的范围作为\(x\)轴上的范围(\(\{(L[u],R[u]),(L[v],R[v])\}\)),也可以是后面的那个作为\(x\)轴上的范围(\(\{(L[v],R[v]),(L[u],R[u])\}\)).所以这里我默认\(x\)轴上修改的那个范围是\(dfs\)序小的,同时查询也将\(x\)轴默认为小的,这样就不会出现路径查询的时候有子路径没有计入答案的问题了.

代码比较长,调了很久(至今调过最久的代码,上一个是维护数列),细节部分可以再好好想想.

#include<bits/stdc++.h>
using namespace std;
const int N = 40000+5;int n, m, q, c[N], ans[N], cntv = 0, value[N], now[N];
int ecnt = 0, last[N], tot;
int idx = 0, size[N], top[N], L[N], R[N], dep[N], son[N], fa[N];struct edge{ int to, nex; }e[N*2];
struct fruits{ int x, y, k, id; }o[N], tq1[N], tq2[N];
struct Updates{ int x, d, u, k, val; }b[N*4], tv1[N*4], tv2[N*4];bool cmpx(Updates a, Updates b){ return a.x < b.x; }bool cmpxx(fruits a, fruits b){ return a.x < b.x; }int gi(){int ans = 0, f = 1; char i = getchar();while(i<'0' || i>'9'){ if(i == '-') f = -1; i = getchar(); }while(i>='0' && i<='9') ans = ans*10+i-'0', i = getchar();return ans * f;
}void add(int x, int y){  e[++ecnt].to = y, e[ecnt].nex = last[x], last[x] = ecnt; }int lowbit(int x){ return x&-x; }
void update(int x, int val){ for(;x<=n;x+=lowbit(x)) c[x] += val; }
int query(int x){int res = 0;for(;x;x-=lowbit(x)) res += c[x];return res;
}void dfs1(int x, int f, int deep){dep[x] = deep, fa[x] = f, size[x] = 1;for(int to, i=last[x];i;i=e[i].nex){to = e[i].to;if(to == f) continue;dfs1(to, x, deep+1), size[x] += size[to];if(size[to] > size[son[x]]) son[x] = to;}
}void dfs2(int x, int tp){L[x] = ++idx, top[x] = tp;if(son[x]) dfs2(son[x], tp);for(int i=last[x];i;i=e[i].nex)if(e[i].to != son[x] && e[i].to != fa[x]) dfs2(e[i].to, e[i].to);R[x] = idx;
}int lcason(int a, int b){while(top[a] != top[b]){if(fa[top[a]] == b) return top[a];a = fa[top[a]];}return son[b];
}void solve(int vl, int vr, int l, int r, int ql, int qr){if(ql > qr) return;if(vl == vr){for(int i=ql;i<=qr;i++) ans[o[i].id] = value[vl];return;}int mid = (vl+vr>>1), cntv1 = 0, cntq1 = 0, cntv2 = 0, cntq2 = 0;int lenv = l-1, lenq = ql-1, sum, pos = l;for(int i=ql;i<=qr;i++){ // i stands for queriesfor(;pos <= r && b[pos].x <= o[i].x; pos++){if(b[pos].val <= value[mid]){tv1[++cntv1] = b[pos];update(b[pos].d, b[pos].k);update(b[pos].u+1, -b[pos].k);} else tv2[++cntv2] = b[pos];}sum = query(o[i].y);if(now[o[i].id]+sum >= o[i].k) tq1[++cntq1] = o[i];else now[o[i].id] += sum, tq2[++cntq2] = o[i];}for(; pos <= r; pos++){if(b[pos].val <= value[mid]){tv1[++cntv1] = b[pos];update(b[pos].d, b[pos].k);update(b[pos].u+1, -b[pos].k);} else tv2[++cntv2] = b[pos];}for(int i=l;i<=r;i++)if(b[i].val <= value[mid])update(b[i].d, -b[i].k), update(b[i].u+1, b[i].k);for(int i=1;i<=cntv1;i++) b[++lenv] = tv1[i];for(int i=1;i<=cntv2;i++) b[++lenv] = tv2[i];for(int i=1;i<=cntq1;i++) o[++lenq] = tq1[i];for(int i=1;i<=cntq2;i++) o[++lenq] = tq2[i];solve(vl, mid, l, l+cntv1-1, ql, ql+cntq1-1);solve(mid+1, vr, l+cntv1, r, ql+cntq1, qr);
}int main(){int x, y, z, val; n = gi(), m = gi(), q = gi();for(int i=1;i<n;i++) x = gi(), y = gi(), add(x, y), add(y, x);dfs1(1, -1, 1), dfs2(1, 1);for(int i=1;i<=m;i++){x = gi(), y = gi(), value[i] = gi();if(dep[x] > dep[y]) swap(x, y); // dep[x] <= dep[y]if(L[x] <= L[y] && L[y] <= R[x]){z = lcason(y, x);if(L[z] > 1){if(L[z]-1 < L[y]){b[++cntv] = (Updates){ 1, L[y], R[y], 1, value[i] };b[++cntv] = (Updates){ L[z], L[y], R[y], -1, value[i] };} else {b[++cntv] = (Updates){ L[y], 1, L[z]-1, 1, value[i] };b[++cntv] = (Updates){ R[y]+1, 1, L[z]-1, -1, value[i] };}}if(R[z]+1 <= n){b[++cntv] = (Updates){ L[y], R[z]+1, n, 1, value[i] };b[++cntv] = (Updates){ R[y]+1, R[z]+1, n, -1, value[i] };}} else {if(L[x] < L[y]){b[++cntv] = (Updates){ L[x], L[y], R[y], 1, value[i] };b[++cntv] = (Updates){ R[x]+1, L[y], R[y], -1, value[i] };} else {b[++cntv] = (Updates){ L[y], L[x], R[x], 1, value[i] };b[++cntv] = (Updates){ R[y]+1, L[x], R[x], -1, value[i] };}}}sort(b+1, b+cntv+1, cmpx), sort(value+1, value+m+1);tot = unique(value+1, value+m+1)-value-1;for(int i=1;i<=q;i++){x = gi(), y = gi(), val = gi();if(L[x] > L[y]) swap(x, y);o[i] = (fruits){ L[x], L[y], val, i };}sort(o+1, o+q+1, cmpxx);solve(1, tot, 1, cntv, 1, q);for(int i=1;i<=q;i++) cout << ans[i] << endl;return 0;
}

```

转载于:https://www.cnblogs.com/BCOI/p/9627467.html

[洛谷P3242] [HNOI2015]接水果相关推荐

  1. P3242 [HNOI2015] 接水果(整体二分、扫描线)

    P3242 [HNOI2015] 接水果 给定一棵树,定义给定了ppp个盘子,每个盘子是树上u,vu, vu,v两点的路径,且盘子有权值,定义水果,水果也是树上u,vu, vu,v两点间的路径. 有q ...

  2. P3242 [HNOI2015] 接水果(整体二分、扫描线、dfs序)

    解析 一道有点毒瘤的题 也是一道感觉真的可以出现在考场上的很综合的题 做的还可以 除了一开始把盘子和水果看反白写了各树套树之外 为什么盘子是水果的子路径啊 由于是做专题爬过来的多次询问区间第k小,想到 ...

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

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

  4. 【Luogu】 P3242 [HNOI2015] 接水果

    题目链接 点击打开链接 题目解法 我们发现一段路径包含另一条不好做,但是被包含的关系就比较简单 对于每个盘子,我们可以分成 2 类情况,这里可以预处理出dfn序,令st[u]为进入u的时间,ed[u] ...

  5. 洛谷P3243 [HNOI2015]菜肴制作——拓扑排序

    题目:https://www.luogu.org/problemnew/show/P3243 正向按字典序拓扑排序很容易发现是不对的,因为并不是序号小的一定先做: 但若让序号大的尽可能放在后面,则不会 ...

  6. 洛谷P3243 [HNOI2015]菜肴制作(思维+反向拓扑)

    传送门 听说原本是紫题 需要先做 i i i 再做 j j j 的限制很自然的让人想到了拓扑排序,然而让编号较小的菜肴尽可能较早输出并不是要求得到最小字典序的答案!! 反面样例可见第三组输入数据,若按 ...

  7. 洛谷 - 试炼场(全部题目备份)

    整理的算法模板合集: ACM模板 目录 1.新手村 1 - 1 洛谷的第一个任务 1 - 2 顺序与分支 1 - 3 循环!循环!循环! 1 - 4 数组 1 - 5 简单字符串 1 - 6 过程函数 ...

  8. 洛谷-题解 P2672 【推销员】

    独门思路!链表加优先队列! 这题一望,贪心是跑不掉了,但是我贪心并不好,所以想到了一个复杂一些但思路更保稳的做法 思路: 1 因为是离线操作,所以我们可以倒着求,先求x=n的情况,因为那样直接就知道了 ...

  9. 洛谷 P1142 轰炸

    洛谷 P1142 轰炸 题目描述 "我该怎么办?"飞行员klux向你求助. 事实上,klux面对的是一个很简单的问题,但是他实在太菜了. klux要想轰炸某个区域内的一些地方,它们 ...

最新文章

  1. java非递归遍历file树_Java语言实现非递归实现树的前中后序遍历总结
  2. 发现个很有意思的angularjs +grunt 复习项目
  3. caffe filter type:Xavier
  4. SAP 调用smartforms打印如何统计实际打印状态和打印次数
  5. JS -- Unexpected trailing comma
  6. js 设置 table 第一个 tr_华胜天成参与起草中国主导的第一个云计算国际技术报告...
  7. linux 安装windows字库,Linux安装windows字体
  8. [渝粤教育] 中国地质大学 金融保险业会计 复习题 (2)
  9. 容易忽视但是功能灰常强大的Java API(五. 二分查找)
  10. ORACLE-016:ora-01720 授权选项对于'xxxx'不存在
  11. erp软件是什么软件
  12. Internal table 备忘
  13. Centos中如何配置Texlive2013中文字体的问题
  14. vptr初始化语义学
  15. AI及相关领域国际会议级别
  16. 批量爬取网易云热歌榜音乐
  17. 使用cmake搭建一个跨平台游戏服务器
  18. 自动驾驶感知——环境感知的基本概念
  19. java如何通过域名查ip_JAVA实现通过IP反查域名
  20. 如何让百度和谷歌收录你的博客

热门文章

  1. 微软进一步融合 Linux,VS Code 官方支持树莓派
  2. 甲骨文谷歌继续打官司:美最高法院同意复审 Java API 版权诉讼案
  3. 微软开源数据处理引擎 Trill,每天可分析万亿次事件
  4. arduino支持python吗_python能给arduino的板子编程吗?stm32支持吗?什么游戏引擎支持python?...
  5. php转go注意,PHP转Golang一些感想
  6. python 获取pv_Python爬虫入门教程 40-100 博客园Python相关40W博客抓取 scrapy
  7. 移除数组第一个元素_3分钟短文 | PHP 数组删除元素,忘了foreach吧,有更优雅的方式...
  8. 橱柜高度与身高对照表_厨房台面高度是多少 厨房台面如何选购
  9. 怎么解log方程_微观动力学解合成氨催化反应TOF
  10. C语言程序密码输入退回,想程序高手求助--用C语言来编辑一个输入密码的程序...