【做题】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, ...
随机推荐
- python3(socket 实现udp,tcp套接字编程)
#coding=utf-8 #客户端程序TCP 连接 import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connec ...
- django 静态css js文件配置
参考:http://blog.csdn.net/liqiancao/article/details/66151287
- IIS8无法通过IP访问解决办法
今天配置在Windows server 2012 R2 上配置IIS8时,出现局域网内无法使用IP访问站点的问题,查找资料依然无法解决.最后发现IIS8配置好主机名后无法使用主机IP访问站点,只能使用 ...
- jQuery工具--$.each()和$.merge()
jQuery.each(object, [callback])或者jQuery(object).each([callback]) 概述 通用遍历方法,可用于遍历对象和数组. 不同于遍历 jQue ...
- Linux基础命令---修改用户信息usermod
usermod 修改用户的信息,包括用户名.密码.家目录.uid等. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法 use ...
- 随笔 js-----------------------------------------------------------------------------------------------------
http://www.cnblogs.com/liuling/p/2014-4-19-04.html redis Base64.encode($( "#byerName").v ...
- Python进阶【第九篇】装饰器
什么是装饰器 装饰器本身就是函数,并且为其他函数添加附加功能 装饰器的原则:1.不修改被装饰对象的源代码 2.不修改被装饰对象的调用方式装饰器=高阶函数+函数嵌套+闭包 # res=timmer(t ...
- maven war工程重命名
1,按f2对项目进行改名 2,改变其web.xml 的项目名 3,org.eclipse.wst.common.component 改变其项目名
- MyEclipse如何修改XML文件默认打开的编辑器
1.MyEclipse如何修改XML文件默认打开的编辑器 Windows--->Preferences--->General--->Editors--->File Associ ...
- Kattis之旅——Prime Path
The ministers of the cabinet were quite upset by the message from the Chief of Security stating that ...