题目:

洛谷 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. 【数轴涂色+并查集路径压缩+加速】C. String Reconstruction

    http://codeforces.com/contest/828/problem/C [题意] [思路] 因为题目保证一定有解,所有优化时间复杂度的关键就是不要重复染色,所以我们可以用并查集维护区间 ...

  2. [K/3Cloud] 如何代码中动态设置当前活动页签

    this.GetControl<TabControl>(key).SelectedIndex=目标Index Ps:如下方式隐藏页签: this.View.GetControl(" ...

  3. NOIP2015提高组D1T3 斗地主

    问一副排n张,n<=23最少打几次打完,数据组数T<=100. 面向数据编程.. 前30分:乱暴力?没有顺子,把单.对子.炸弹.三张.王炸.三带一判一次即可. 前70分:状压,先预处理哪些 ...

  4. msp430入门编程0

    msp430单片机最小系统 msp430入门学习 msp430入门编程

  5. Git Cheat Sheet 中文版

    Git Cheat Sheet 中文版 索引 配置 配置文件 创建 本地修改 搜索 提交历史 分支与标签 更新与发布 合并与重置 撤销 Git Flow 配置 列出当前配置: $ git config ...

  6. jquery控制Request Payload和Form Data

    Request Payload方式,会发起两次请求 Form Data只发起一次请求 若要把一个ajax请求改为Payload方式,设置contentType即可,发现请求参数不是对象,再把参数转换为 ...

  7. 激活IDEA 2019.1

    First step: 先下载jar包JetbrainsCrack.jar,把它放到你认为合适的文件夹内, 我放在了安装目录C:\Program Files\JetBrains\IntelliJ ID ...

  8. Hadoop 执行过程中出现 name node is in safe mode 问题

    解决方法: 1.进入hadoop安装根目录 如 :我的hadoop 安装在/usr/local/hadoop 执行 cd /usr/local/hadoop bin/hadoop dfsadmin - ...

  9. form 表单序列化 serialize

    在开发中有时需要在js中提交form表单数据,就需要将form表单进行序列化. jquery提供的serialize方法能够实现. $("#searchForm").seriali ...

  10. 【WinHec启发录】透过Windows 10技术布局,谈微软王者归来

    每一个时代都有王者,王者的成功,往往是由于恰逢其时地公布了一个成功的产品(具有里程碑意义,划时代的产品).Windows 95的成功标示着微软是PC时代的王者:WinXP的成功标示着微软是互联网时代的 ...