【BZOJ4009_洛谷3242】[HNOI2015] 接水果(整体二分)
题目:
分析:
明确题意:在一棵树上给定若干权值为 \(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}\) ):
\]
否则需要满足( \(p\) 的含义见上):
\]
对于求第 \(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] 接水果(整体二分)的更多相关文章
- [洛谷P3242] [HNOI2015]接水果
洛谷题目链接:[HNOI2015]接水果 题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简 ...
- 洛谷 P3242 [HNOI2015]接水果 解题报告
P3242 [HNOI2015]接水果 题目描述 风见幽香非常喜欢玩一个叫做 \(osu!\) 的游戏,其中她最喜欢玩的模式就是接水果.由于她已经\(DT\) \(FC\) 了\(\tt{The\ b ...
- ●洛谷P3242 [HNOI2015]接水果
题链: https://www.luogu.org/problemnew/show/P3242 题解: 整体二分,扫描线+树状数组. 详细的题解:http://blog.csdn.net/thy_as ...
- BZOJ.4009.[HNOI2015]接水果(整体二分 扫描线)
LOJ BZOJ 洛谷 又是一个三OJ rank1!=w= \(Description\) (还是感觉,为啥非要出那种题目背景啊=-=直接说不好么) 给定一棵树和一个路径集合(每条路径有一个权值).\ ...
- [BZOJ4009][HNOI2015]接水果(整体二分)
[HNOI2015]接水果 时间限制:60s 空间限制:512MB 题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果. 由于她已经DT FC 了The b ...
- [bzoj4009] [HNOI2015]接水果 整体二分+扫描线+dfs序+树状数组
Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果. 由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更 加 ...
- [HNOI2015]接水果[整体二分]
[HNOI2015]接水果 给出一个树上路径集合\(S\) 多次询问\(x,y\)中的\(k\)小值 如果你问我数列上那么我会 树上的话 树上差分了吧直接?- 令 \(st_x<st_y\) 1 ...
- BZOJ4009:[HNOI2015]接水果(整体二分版)
浅谈离线分治算法:https://www.cnblogs.com/AKMer/p/10415556.html 题目传送门:https://lydsy.com/JudgeOnline/problem.p ...
- 洛谷P3527 [POI2011]MET-Meteors(整体二分)
传送门 整体二分 先二分一个答案,判断是否可行,把可行的全都扔到左边,不可行的扔到右边 判断是否可行用树状数组就行 具体细节看代码好了 整体二分细节真多……也可能是我大脑已经退化了? //minamo ...
随机推荐
- JS前端取得并解析后台服务器返回的JSON数据的方法
摘要:主要介绍:使用eval函数解析JSON数据:$.getJSON()方法获得服务器返回的JSON数据 JavaScript eval() 函数 eval(string) 函数可计算某个字符串,并执 ...
- POJ 3279 Fliptile【枚举】
题意: 又是农夫和牛的故事...有m*n个黑白块,黑块的背面是白块,白块背面是黑块,一头牛踩一块,则这个块的上下左右的方块都会转动,问至少踩多少块,才会使所有块都变成白色? 分析: 还是开关问题,同样 ...
- Nginx教程收集
学习要系统,最推荐的方式是看书. 下面是收集的一些Nginx教程: https://www.gitbook.com/book/yinsigan/nginx/details http://www.ngi ...
- x86CPU 实模式 保护模式 傻傻分不清楚? 基于Xv6-OS 分析CR0 寄存器
基于Xv6-OS 分析CR0 寄存器 之前一直认为晕乎乎的...啥?什么时候切换real model,怎么切换,为什么要切换? ------------------------------------ ...
- 两个月后才更新一篇。。。。LIB和DLL的差别
共同拥有两种库: 一种是LIB包括了函数所在的DLL文件和文件里函数位置的信息(入口).代码由执行时载入在进程空间中的DLL提供,称为动态链接库dynamic link library. 一种是 ...
- 【Linux多线程】三个经典同步问题
在了解了<同步与互斥的区别>之后,我们来看看几个经典的线程同步的例子.相信通过具体场景可以让我们学会分析和解决这类线程同步的问题,以便以后应用在实际的项目中. 一.生产者-消费者问题 问题 ...
- StringBuffer疑问
为何结果为AB.B? public static void main(String[] args) { StringBuffer a=new StringBuffer("A"); ...
- 科普:google的数字图书馆
https://books.google.com/ngrams Google Ngram Viewer,她利用google所拥有的所有图书作为资源,为你提供单词和短语历年使用次数的展示图标.数据化了数 ...
- java web项目的部署
java web项目的部署 我刚开始学着编写java web项目,着实遇到不少麻烦,感觉JAVA真难侍候,好多东西都是手动.手动. 就拿这个web项目在tomcat上的部署来说吧.我在项目的build ...
- 关于ServerSocketChannel和SocketChannel
1 这两个类是抽象类 源码里面它们的前面是加了abstract的. 2 抽象类是不能new出实例的 3 这两个类使用静态方法open创建其子类的实例 有动态绑定原理可知,返回的ServerSocket ...