【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 ...
随机推荐
- Flask组件:flask-sqlalchemy & flask-script & flask-migrate
flask-sqlalchemy组件 项目目录结构: flask目录 # 项目名 |--- flaskdir |--- static # 静态文件 |--- templates # 模板 |--- m ...
- 关于android系统启动不同activity默认过渡动画不同的一些认识
在同一个android设备里,发现不同的app启动时显示的过渡动画是不同的.查看显示不同过渡动画的两个app的源码,其设置的主题都是同一个主题,但是为什么过渡动画效果不同呢?后来发现,activity ...
- Java Web 总结
Java Servlet 总结 Servlet 简介 Servlet 是什么? 运行在Web服务器上的应用程序,作为浏览器和服务器之间的中间层. 与CGI功能类似,优点在于 性能更好 在Web服务器的 ...
- Parent and son
Give you a tree with N vertices and N‐ 1 edges, and then ask you Q queries on “which vertex is Y's s ...
- SOJ 2930_积木城堡
[题意]若干个城堡,给定每个城堡的积木数及每块积木的棱长.从城堡中抽出积木使每块城堡高度相同,求最大高度 [分析]城堡的积木选择可以看成01背包问题,从最矮的城堡高度开始依次递减,求出使每个背包都能装 ...
- Ubuntu 16.04添加多张虚拟网卡
1.添加 sudo ifconfig enp0s31f6:0 192.168.10.10 up 2.卸载 sudo ifconfig enp0s31f6:0 down 注意:enp0s31f6每台电脑 ...
- Hive之Order,Sort,Cluster and Distribute By
测试数据 create table sort_test( id int, name string ) row format delimited fields terminated by '\t' li ...
- MongoDB小结09 - update【定位修改器】
如果要操作数组中的值,可以用值在数组中的位置当做参数来删除 db.user.update({"name":"codingwhy.com"},{"$se ...
- ETL全量多表同步简述
ETL全量多表同步简述 1. 实现需求 当原数据库的表有新增.更新.删除操作时,将改动数据同步到目标库对应的数据表. 2. 设计思路 设计总体流程图如下: 1.获取同步表名如下图: 2.循环迁移数据如 ...
- 使用NPOI将DataTable生成Excel
听闻npoi 2.0版本支持excel2007格式了,表示期待其表现.不过目前还是使用1.2.5稳重点. 生活中有太多的列表都需要一个导出功能,当然这里的生活指的的程序员的生活.DataTable是从 ...