洛谷题目链接:[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])\}\)

而这个影响的范围正好对应了二维平面上的一个矩形.

所以对于查询的一条路径可以直接在二维平面上单点查询.

  1. 若\(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 queries
for(;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;
}

[洛谷P3242] [HNOI2015]接水果的更多相关文章

  1. 洛谷 P3242 [HNOI2015]接水果 解题报告

    P3242 [HNOI2015]接水果 题目描述 风见幽香非常喜欢玩一个叫做 \(osu!\) 的游戏,其中她最喜欢玩的模式就是接水果.由于她已经\(DT\) \(FC\) 了\(\tt{The\ b ...

  2. ●洛谷P3242 [HNOI2015]接水果

    题链: https://www.luogu.org/problemnew/show/P3242 题解: 整体二分,扫描线+树状数组. 详细的题解:http://blog.csdn.net/thy_as ...

  3. 洛谷P3242 接水果 [HNOI2015] 整体二分

    正解:整体二分+树状数组 解题报告: 传送门! 题目还是大概解释下?虽然其实是看得懂的来着,,, 大概就是说给一棵树.给定一些询问,每个询问都是说在两个点之间的路径上的子路径的第k大是什么 然后看到这 ...

  4. 洛谷P3242 接水果

    关于矩形与点其实有两种关系. 一种是每个矩形包含多少点.一种是每个点被多少矩形包含. 解:因为可以离线所以直接套整体二分.关键是考虑如何能够被覆盖. 我一开始都是想的树上操作...其实是转化成DFS序 ...

  5. [洛谷 P3239] [HNOI2015]亚瑟王

    [HNOI2015]亚瑟王 题目描述 小 K 不慎被 LL 邪教洗脑了,洗脑程度深到他甚至想要从亚瑟王邪教中脱坑.他决定,在脱坑之前,最后再来打一盘亚瑟王.既然是最后一战,就一定要打得漂亮.众所周知, ...

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

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

  7. 洛谷P3244 [HNOI2015]落忆枫音

    #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #in ...

  8. luogu P3242 [HNOI2015]接水果

    传送门 其实这题难点在于处理路径包含关系 先求出树的dfn序,现在假设路径\(xy\)包含\(uv(dfn_x<dfn_y,dfn_u<dfn_v)\) 如果\(lca(u,v)!=u\) ...

  9. 洛谷P3243 [HNOI2015]菜肴制作 拓扑排序+贪心

    正解:拓扑排序 解题报告: 传送门! 首先看到它这个约束就应该要想到拓扑排序辣QwQ 首先想到的应该是用优先队列代替队列,按照节点编号排序 然后也很容易被hack:<5,1> 正解应为5, ...

随机推荐

  1. Python3 数据类型-集合

    在Python中集合set是基本数据类型的一种,它有可变集合(set)和不可变集合(frozenset)两种.创建集合set.集合set添加.集合删除.交集.并集.差集的操作都是非常实用的方法. 集合 ...

  2. es6从零学习(二):promise

    es6从零学习(二):promise 一:promise的由来 某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱.由此,Promise的概念就由社区 ...

  3. CentOS Openvpn搭建以及 linux&&windows客户端的连接

    本文参考:http://www.centoscn.com/CentosServer/test/2014/1120/4153.html 一. Server安装准备     (CentOS release ...

  4. 团队Beta阶段事后分析

    团队Beta阶段事后分析 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件要解决用户的休闲娱乐问题,为用户提供好玩的模拟经营类的游戏,游戏主题 ...

  5. java—连连看GUI

    1.连连看棋盘图形化 package Link; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; impo ...

  6. UBUNTU如何安装tar.gz版的flash

    adobe flash player的官方下载页面为:https://get.adobe.com/cn/flashplayer/ 不过近期通过APT方式以及ubuntu的软件中心都安装不了flashp ...

  7. lintcode-28-搜索二维矩阵

    搜索二维矩阵 写出一个高效的算法来搜索 m × n矩阵中的值. 这个矩阵具有以下特性: 每行中的整数从左到右是排序的. 每行的第一个数大于上一行的最后一个整数. 样例 考虑下列矩阵: [ [1, 3, ...

  8. Spring Boot(三)自动装配

    @Configuration和@Bean Spring提供了注解@Configuration和@Bean注解用来配置多个Bean,在以前的Spring项目中可以通过xml的方式配置: <bean ...

  9. hive mapjoin优化

    默认为10MB,如果大于该值不会执行mapjoin,hive语句中直接设置的mapjoin也不再起作用. 参考hive wiki把hive.auto.convert.join.noconditiona ...

  10. cacti添加多个tomcat监控(多端口)

    1.修改tomcat的模版 Data Input Methods->Tomcat Status 把原本固定的端口,用户名和密码手动修改成变量(绿线标出的),之后save保存之后,再在Input ...