【做题】agc002D - Stamp Rally——整体二分的技巧
题意:给出一个无向连通图,有\(n\)个顶点,\(m\)条边。有\(q\)次询问,每次给出\(x,y,z\),最小化从\(x\)和\(y\)开始,总计访问\(z\)个顶点(一个顶点只计算一次),经过的边的编号的最大值。
\(n,m,q \leq 10^5\)
这道题难度不大。考虑单次询问,答案显然是可以二分的。二分答案后,就只用求出\(x\)和\(y\)所在联通块的大小之和了(\(x\)和\(y\)联通特判)。多次询问,就用整体二分。这样并查集需要支持撤回操作,所以还需要一个\(O(\log n)\)。于是,我们得到了\(O(n \log^2 n)\)的算法。
写这篇题解的原因在于这里可以使用一个整体二分的技巧来优化复杂度。首先,这道题边是在所有询问前就加好的,也就是说修改和询问之间是没有先后关系的。(实际上,更抽象地说,问题只受一个因素影响。像修改和询问之间是没有先后关系,是说问题既受答案大小,还受时间先后影响)考虑我们整体二分的每一层,所有的询问被划分为若干个区间,每个区间都对应一个答案的区间。在整体二分中,我们取每个区间的答案区间的中点,然后将其划分为两个区间。注意到,从左往右这个中点是单调递增的。也就是说,我们可以在bfs地进行整体二分,在每一层从左往右处理区间,就能避免并查集的撤回操作,只用在每一层开始时清空就可以了。
时间复杂度\(O(n \log n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n,m,q,uni[N],ans[N],sz[N];
struct edge {
int a,b;
} edg[N];
int getfa(int pos) {
return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]);
}
void join(int a,int b) {
a = getfa(a);
b = getfa(b);
if (a == b) return;
if (sz[a] > sz[b]) swap(a,b);
uni[a] = b;
sz[b] += sz[a];
}
int calc(int a,int b) {
a = getfa(a), b = getfa(b);
if (a == b) return sz[a];
return sz[a] + sz[b];
}
struct data {
int x,y,z,id;
};
struct Divide {
int l,r,ansl,ansr;
};
vector<data> que[2];
vector<Divide> Div[2];
int main() {
int x,y,z;
scanf("%d%d",&n,&m);
for (int i = 1 ; i <= m ; ++ i) {
scanf("%d%d",&x,&y);
edg[i] = (edge) {x,y};
}
scanf("%d",&q);
for (int i = 1 ; i <= q ; ++ i) {
scanf("%d%d%d",&x,&y,&z);
que[0].push_back((data) {x,y,z,i});
}
Div[0].push_back((Divide) {0,q-1,0,m});
int p = 0;
while (Div[p].size()) {
int cur = 1;
Div[p^1].clear();
que[p^1].clear();
for (int i = 1 ; i <= n ; ++ i)
uni[i] = i, sz[i] = 1;
for (int i = 0 ; i < (int)Div[p].size() ; ++ i) {
int num = 0;
if (Div[p][i].ansl == Div[p][i].ansr) {
for (int j = Div[p][i].l ; j <= Div[p][i].r ; ++ j)
ans[que[p][j].id] = Div[p][i].ansl;
continue;
}
int mid = (Div[p][i].ansl + Div[p][i].ansr) >> 1;
for ( ; cur <= mid ; ++ cur)
join(edg[cur].a,edg[cur].b);
for (int j = Div[p][i].l ; j <= Div[p][i].r ; ++ j)
if (calc(que[p][j].x,que[p][j].y) >= que[p][j].z)
que[p^1].push_back(que[p][j]), ++ num;
if (num) Div[p^1].push_back((Divide) {(int)que[p^1].size()-num,(int)que[p^1].size()-1,Div[p][i].ansl,mid});
num = 0;
for (int j = Div[p][i].l ; j <= Div[p][i].r ; ++ j)
if (calc(que[p][j].x,que[p][j].y) < que[p][j].z)
que[p^1].push_back(que[p][j]), ++ num;
if (num) Div[p^1].push_back((Divide) {(int)que[p^1].size()-num,(int)que[p^1].size()-1,mid+1,Div[p][i].ansr});
}
p ^= 1;
}
for (int i = 1 ; i <= q ; ++ i)
printf("%d\n",ans[i]);
return 0;
}
小结:涨知识了。巧妙的技巧,总是无穷无尽的。
【做题】agc002D - Stamp Rally——整体二分的技巧的更多相关文章
- [AGC002D] Stamp Rally 整体二分+并查集
Description 给你一个n个点m个条边构成的简单无向连通图,有Q组询问,每次询问从两个点x,y走出两条路径,使这两条路径覆盖z个点,求得一种方案使得路径上经过的变的最大编号最小. Input ...
- [AGC002D] Stamp Rally
确实有想到重构树,不过没有继续下去的思路. 可能是对重构树的性质不太懂. 这种题目我们可以二分答案,考虑怎么\(check\)呢,整体二分+并查集,建出重构树,找去第一个小于这个数的方点,查询他的子树 ...
- [AGC002D] Stamp Rally (并查集+整体二分)
Description 给你一个n个点m个条边构成的简单无向连通图,有Q组询问,每次询问从两个点x,y走出两条路径,使这两条路径覆盖z个点,求得一种方案使得路径上经过的变的最大编号最小. Input ...
- 【学时总结】◆学时·IX◆ 整体二分
◆学时·IX◆ 整体二分 至于我怎么了解到这个算法的……只是因为发现一道题,明显的二分查找,但是时间会爆炸,被逼无奈搜题解……然后就发现了一些东西QwQ ◇ 算法概述 整体二分大概是把BFS与二分查找 ...
- POJ 2104:K-th Number(整体二分)
http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...
- CDQ分治与整体二分小结
前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完 ...
- BZOJ 1901 Zju2112 Dynamic Rankings ——整体二分
[题目分析] 上次用树状数组套主席树做的,这次用整体二分去水. 把所有的查询的结果一起进行二分,思路很好. [代码] #include <cstdio> #include <cstr ...
- 洛谷P3527 MET-Meteors [POI2011] 整体二分
正解:整体二分 解题报告: 传送门! 还有个双倍经验!(明明是一样的题目为什么你们一个紫一个黑啊喂! 这题首先要想到可以二分嘛,然后看到多组询问肯定就整体二分鸭 那就是基本套路啊,发现是区间修改单点查 ...
- 【BZOJ4009】[HNOI2015]接水果 DFS序+整体二分+扫描线+树状数组
[BZOJ4009][HNOI2015]接水果 Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, ...
随机推荐
- idea软件上设置爱彼迎字体
- 如何注销Sitecore CMS
登录Sitecore很容易,但是在旧版本的Sitecore中使用不同的界面,退出可能会给未经证实的人带来挑战. Sitecore 8 Sitecore 6和7 Sitecore 8 Sitecore ...
- HTML标签-----article、aside、figure、nav和section
article <article> 标签定义独立的内容 <!DOCTYPE html> <html> <head> <meta cha ...
- Nginx技术研究系列2-基于Redis实现动态路由
上篇博文我们写了个引子: Ngnix技术研究系列1-通过应用场景看Nginx的反向代理 发现了新大陆,OpenResty OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台 ...
- JS神经网络deeplearn.js:浏览器端机器智能框架
JS神经网络deeplearn.js:浏览器端机器智能框架 通过 deeplearn.js,可以实现在浏览器中训练神经网络模型,也可在推理阶段运行预训练模型. deeplearn.js 以 TypeS ...
- 关于Vue中的 render: h => h(App) 具体是什么含义?
render: h => h(App) 是下面内容的缩写: render: function (createElement) { return createElement(App); } 进一步 ...
- 查询在某一个时间段内的sql(oracel)
( to_char(t.TUIJIAN_TIME, 'yyyy-MM-dd') between #{begin_time} and #{end_time} )
- SpringMVC中参数接收
/** * * SpringMVC中参数接收 * 1.接收简单类型 int String * 2.可以使用对象pojo接收 * 3.可以使用集合数据接收参数 * 页面: name="ids ...
- Django之Ajax刷新记住用户名
使用Cookie方法记住用户名 1.创建登录路由 2.定义登录视图函数 判断cookie中是否存在username,存在就将username渲染到login.html模板页面中 3.进行Ajax验证 ...
- #mxnet# 权值共享
https://www.cnblogs.com/chenyliang/p/6847744.html Note:后记此权值共享非彼卷积共享.说的是layer实体间的参数共享. Introduction ...