题目:

洛谷 3242

分析:

明确题意:在一棵树上给定若干权值为 \(w\) 的路径 \((u,v)\) (盘子),每次给定 \((a,b)\) (水果),询问所有满足 \((u,v)\) 被 \((a,b)\) 完全覆盖的路径中第 \(k\) 小的路径的权值。

先考虑如何快速判断 \((u,v)\) 是不是 \((a,b)\) 的子路径。第一反应是充要条件是 \(a\) 和 \(b\) 分别在 \(u\) 和 \(v\) 的子树中(设 \(\mathrm{dfn}_p\) 是 \(p\) 在 dfs 序中的位置,以下默认 \(\mathrm{dfn}_u<\mathrm{dfn}_v\) 且 \(\mathrm{dfn}_a<\mathrm{dfn}_b\) )。这对于大部分情况是成立的,但是当 \(u\) 是 \(v\) 的祖先时并不成立,因为 \(v\) 的子树被包含在 \(u\) 的子树中,所以而在 \(v\) 的子树中任选两点都满足条件,但之间的路径并不能覆盖 \((u,v)\) 。画图可知,此时 \(b\) 仍要求在 \(v\) 的子树中,而 \(a\) 应当在 \(p\) 的子树外(注意不是 \(u\) 的子树),其中 \(p\) 是从 \(u\) 到 \(v\) 的路径上除 \(u\) 之外的第一个点。详见下图(博客特色画风):

总结一下,如果 \((u,v)\) 被 \((a,b)\) 完全覆盖,那么需要满足:

如果 \((u,v)\) 不是一条深度递增的链,那么需要满足( \(\mathrm{out}_p\) 表示 \(p\) 的子树中最大的 \(\mathrm{dfn}\) ):

\[\mathrm{dfn}_a\in [\mathrm{dfn}_u,\mathrm{out}_u]\ 且\ \mathrm{dfn}_b\in [\mathrm{dfn}_v,\mathrm{out}_v]
\]

否则需要满足( \(p\) 的含义见上):

\[\mathrm{dfn}_a\in [1,\mathrm{dfn}_p)\cup (\mathrm{out}_p,n]\ 且\ \mathrm{dfn}_b\in [\mathrm{dfn}_v,\mathrm{out}_v]
\]

对于求第 \(k\) 小的题,一个很常见的策略是二分。二分一个答案 \(x\) ,把所有权值小于等于 \(x\) 的点都插入数据结构,然后查询符合要求的点的数量,若大于等于 \(k\) 则向下二分,否则向上二分。

对于这道题,由于限制是二维的,所以需要树套树。插入一个路径 \((u,v)\) 相当于给它能贡献到的地方的答案全部加 1 。对于 \(u\) 不是 \(v\) 的祖先的情况,这个「地方」是一维范围为 \([\mathrm{dfn}_u,\mathrm{out}_u]\) ,另一维范围为 \([\mathrm{dfn}_v,\mathrm{out}_v]\) 的矩形;否则,贡献到的是两个矩形(自行根据上面的式子构造)。查询一个路径 \((a,b)\) 相当于查询点 \((\mathrm{dfn}_a,\mathrm{dfn}_b)\) 的权值。由于是多组询问,所以套上整体二分,时间复杂度 \(O(n\log^3 n)\) 。

然而实测树套树会不幸被 卡常 卡复杂度。可以用类似离线扫描线的思想,把修改和询问按第一维的 dfn 排序,然后按 dfn 从小到大处理,并把修改拆成两次: 在 \(\mathrm{dfn}_u\) 处给 \([\mathrm{dfn}_v,\mathrm{out}_v]\) 加 1 ,在 \(\mathrm{out}_u+1\) 处给上述区间减 1 。用树状数组维护区间修改和单点查询,时间复杂度 \(O(n\log^2 n)\) 。

代码:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std; namespace zyt
{
template<typename T>
inline bool read(T &x)
{
char c;
bool f = false;
x = 0;
do
c = getchar();
while (c != EOF && c != '-' && !isdigit(c));
if (c == EOF)
return true;
if (c == '-')
f = true, c = getchar();
do
x = x * 10 + c - '0', c = getchar();
while (isdigit(c));
if (f)
x = -x;
return true;
}
template<typename T>
inline void write(T x)
{
static char buf[20];
char *pos = buf;
if (x < 0)
putchar('-'), x = -x;
do
*pos++ = x % 10 + '0';
while (x /= 10);
while (pos > buf)
putchar(*--pos);
}
const int N = 4e4 + 10, B = 16;
int n, head[N], ecnt, fa[N][B], dep[N], dfn[N], dfncnt, out[N], id[N], ans[N];
struct edge
{
int to, next;
}e[N << 1];
struct _plt
{
int u, v, c, nxt;
bool operator < (const _plt &b) const
{
return c < b.c;
}
}plt[N]; // plate
int idplt[N];
struct _frt
{
int u, v, k, id;
}frt[N]; // fruit
bool cmpdfnu(const int a, const int b)
{
return dfn[frt[a].u] < dfn[frt[b].u];
}
namespace Tree_Array
{
int data[N];
inline int lowbit(const int x)
{
return x & (-x);
}
void add(int a, const int x)
{
while (a <= n)
data[a] += x, a += lowbit(a);
}
int query(int a)
{
int ans = 0;
while (a)
ans += data[a], a -= lowbit(a);
return ans;
}
void add(const int l, const int r, const int x)
{
add(l, x), add(r + 1, -x);
}
}
void add(const int a, const int b)
{
e[ecnt] = (edge){b, head[a]}, head[a] = ecnt++;
}
void dfs(const int u, const int f)
{
fa[u][0] = f;
for (int i = 1; i < B; i++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
dep[u] = dep[f] + 1;
dfn[u] = ++dfncnt;
for (int i = head[u]; ~i; i = e[i].next)
{
int v = e[i].to;
if (v == f)
continue;
dfs(v, u);
}
out[u] = dfncnt;
}
inline int lca(int a, int b)
{
if (dep[a] < dep[b])
swap(a, b);
for (int i = B - 1; i >= 0; i--)
if (dep[fa[a][i]] >= dep[b])
a = fa[a][i];
if (a == b)
return a;
for (int i = B - 1; i >= 0; i--)
if (fa[a][i] != fa[b][i])
a = fa[a][i], b = fa[b][i];
return fa[a][0];
}
namespace Divide_Together
{
struct _mdf
{
int pos, l, r, x;
_mdf() {}
_mdf(const int _pos, const int _l, const int _r, const int _x)
: pos(_pos), l(_l), r(_r), x(_x) {}
bool operator < (const _mdf &b) const
{
return pos < b.pos;
}
}mdf[N << 1];
int cnt;
void insert(const int l, const int r, const int ll, const int rr, const int x)
{
mdf[cnt++] = _mdf(l, ll, rr, x);
mdf[cnt++] = _mdf(r + 1, ll, rr, -x);
}
void insert(const int i, const int x)
{
if (plt[i].nxt)
{
insert(1, dfn[plt[i].nxt] - 1, dfn[plt[i].v], out[plt[i].v], x);
if (out[plt[i].nxt] < n)
insert(dfn[plt[i].v], out[plt[i].v], out[plt[i].nxt] + 1, n, x);
}
else
insert(dfn[plt[i].u], out[plt[i].u], dfn[plt[i].v], out[plt[i].v], x);
}
void solve(const int l, const int r, const int ql, const int qr)
{
using Tree_Array::query;
using Tree_Array::add;
if (l == r)
{
for (int i = ql; i <= qr; i++)
ans[frt[id[i]].id] = plt[l].c;
return;
}
static int buf1[N], buf2[N];
int cnt1 = 0, cnt2 = 0;
int mid = (l + r) >> 1;
cnt = 0;
for (int i = l; i <= mid; i++)
insert(i, 1);
sort(mdf, mdf + cnt);
int now = 0;
for (int i = ql; i <= qr; i++)
{
while (now < cnt && mdf[now].pos <= dfn[frt[id[i]].u])
add(mdf[now].l, mdf[now].r, mdf[now].x), ++now;
int tmp = query(dfn[frt[id[i]].v]);
if (tmp >= frt[id[i]].k)
buf1[cnt1++] = id[i];
else
buf2[cnt2++] = id[i], frt[id[i]].k -= tmp;
}
for (int i = 0; i < cnt1; i++)
id[i + ql] = buf1[i];
for (int i = 0; i < cnt2; i++)
id[i + cnt1 + ql] = buf2[i];
for (int i = 0; i < now; i++)
add(mdf[i].l, mdf[i].r, -mdf[i].x);
solve(l, mid, ql, ql + cnt1 - 1);
solve(mid + 1, r, ql + cnt1, qr);
}
}
int work()
{
int p, q;
read(n), read(p), read(q);
memset(head, -1, sizeof(int[n + 1]));
for (int i = 1; i < n; i++)
{
int a, b;
read(a), read(b);
add(a, b), add(b, a);
}
dfs(1, 0);
for (int i = 0; i < p; i++)
{
read(plt[i].u), read(plt[i].v), read(plt[i].c);
if (dfn[plt[i].u] > dfn[plt[i].v])
swap(plt[i].u, plt[i].v);
plt[i].nxt = 0;
if (lca(plt[i].u, plt[i].v) == plt[i].u)
{
int &tmp = plt[i].nxt;
tmp = plt[i].v;
for (int j = B - 1; j >= 0; j--)
if (dep[fa[tmp][j]] > dep[plt[i].u])
tmp = fa[tmp][j];
}
}
sort(plt, plt + p);
for (int i = 0; i < q; i++)
{
id[i] = i;
read(frt[i].u), read(frt[i].v), read(frt[i].k);
if (dfn[frt[i].u] > dfn[frt[i].v])
swap(frt[i].u, frt[i].v);
frt[i].id = i;
}
sort(id, id + q, cmpdfnu);
Divide_Together::solve(0, p - 1, 0, q - 1);
for (int i = 0; i < q; i++)
write(ans[i]), putchar('\n');
return 0;
}
}
int main()
{
return zyt::work();
}

【BZOJ4009_洛谷3242】[HNOI2015] 接水果(整体二分)的更多相关文章

  1. [洛谷P3242] [HNOI2015]接水果

    洛谷题目链接:[HNOI2015]接水果 题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简 ...

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

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

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

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

  4. BZOJ.4009.[HNOI2015]接水果(整体二分 扫描线)

    LOJ BZOJ 洛谷 又是一个三OJ rank1!=w= \(Description\) (还是感觉,为啥非要出那种题目背景啊=-=直接说不好么) 给定一棵树和一个路径集合(每条路径有一个权值).\ ...

  5. [BZOJ4009][HNOI2015]接水果(整体二分)

    [HNOI2015]接水果 时间限制:60s      空间限制:512MB 题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果. 由于她已经DT FC 了The b ...

  6. [bzoj4009] [HNOI2015]接水果 整体二分+扫描线+dfs序+树状数组

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

  7. [HNOI2015]接水果[整体二分]

    [HNOI2015]接水果 给出一个树上路径集合\(S\) 多次询问\(x,y\)中的\(k\)小值 如果你问我数列上那么我会 树上的话 树上差分了吧直接?- 令 \(st_x<st_y\) 1 ...

  8. BZOJ4009:[HNOI2015]接水果(整体二分版)

    浅谈离线分治算法:https://www.cnblogs.com/AKMer/p/10415556.html 题目传送门:https://lydsy.com/JudgeOnline/problem.p ...

  9. 洛谷P3527 [POI2011]MET-Meteors(整体二分)

    传送门 整体二分 先二分一个答案,判断是否可行,把可行的全都扔到左边,不可行的扔到右边 判断是否可行用树状数组就行 具体细节看代码好了 整体二分细节真多……也可能是我大脑已经退化了? //minamo ...

随机推荐

  1. JAVA WEB接口开发简述

    目录 1. JAVA WEB接口开发简述 1.1. 基本了解 1.2. 提供接口 1.3. 调用接口 1. JAVA WEB接口开发简述 1.1. 基本了解 当我们想去访问其他网站的接口时候,而又不想 ...

  2. mybatis collection标签和association标签(一对多,一对一)转载

    mybatis 一对一与一对多collection和association的使用   在mybatis如何进行一对一.一对多的多表查询呢?这里用一个简单的例子说明. 一.一对一 1.associati ...

  3. javascript 閉包

    這兩種寫法都是可以的. 第一種: function a(){ var m=[]; for(var i=1; i<10; i++){ (function(i){ function b(){ con ...

  4. jQuery异步框架探究1:jQuery._Deferred方法

    jQuery异步框架应用于jQuery数据缓存模块.jQuery ajax模块.jQuery事件绑定模块等多个模块,是jQuery的基础功能之中的一个.实际上jQuery实现的异步回调机制能够看做ja ...

  5. php删除数组中指定值的元素

    php删除数组中指定值的元素 /** * 删除数组中指定值的元素 * @author: ibrahim * @param array $arr 数组 * @param string $val 值 * ...

  6. linux下常用快捷方式

    一.终端最常用的快捷键: 1.新建终端窗口:crtl+shift+n 2.终端的切换:shift+左右箭头 3.挂起:crtl+s 4.解除挂起:crtl+q 5.清屏:crtl+l 二.命令行光标移 ...

  7. Android上拉查看详情实现

    京东淘宝有那么一种效果就是,上拉能够查看宝贝的详情,这里我也实现了一个类似的效果,也能够移植到商业项目上:先看看简单的效果图 实现原理事实上是利用了ScrollView的滚动和view的touch事件 ...

  8. Android Api Demos登顶之路(四十五)Loader--&gt;Cursor

    这个demo演示了类载入器的用法.关于类载入器的使用我们在前面的demo中已经介绍过了 在此再小小的复习一下. 类载入器的使用步骤: * 1.获取类载入器的管理者LoaderManager manag ...

  9. java设计模式 -------- 行为模式 之 策略模式(4)

    [本文是自己学习所做笔记.欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020] 上面3节实现了从最初的对整形数组排序到最后能够对全部类型都能够依据须要定义自 ...

  10. 一条SQL语句面试题:求选修所有课程的学生

    前几天求职面试,有一道SQL题:给出三个表:学生.课程.成绩,求选修了所有课程的学生. 一道看似很简单的问题,把我难住了,我改了又改,涂涂画画,抓耳挠腮,因为试卷没有多少空白位置了,最后只好放弃.心情 ...