题意:给出一个无向连通图,有\(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——整体二分的技巧的更多相关文章

  1. [AGC002D] Stamp Rally 整体二分+并查集

    Description 给你一个n个点m个条边构成的简单无向连通图,有Q组询问,每次询问从两个点x,y走出两条路径,使这两条路径覆盖z个点,求得一种方案使得路径上经过的变的最大编号最小. Input ...

  2. [AGC002D] Stamp Rally

    确实有想到重构树,不过没有继续下去的思路. 可能是对重构树的性质不太懂. 这种题目我们可以二分答案,考虑怎么\(check\)呢,整体二分+并查集,建出重构树,找去第一个小于这个数的方点,查询他的子树 ...

  3. [AGC002D] Stamp Rally (并查集+整体二分)

    Description 给你一个n个点m个条边构成的简单无向连通图,有Q组询问,每次询问从两个点x,y走出两条路径,使这两条路径覆盖z个点,求得一种方案使得路径上经过的变的最大编号最小. Input ...

  4. 【学时总结】◆学时·IX◆ 整体二分

    ◆学时·IX◆ 整体二分 至于我怎么了解到这个算法的……只是因为发现一道题,明显的二分查找,但是时间会爆炸,被逼无奈搜题解……然后就发现了一些东西QwQ ◇ 算法概述 整体二分大概是把BFS与二分查找 ...

  5. POJ 2104:K-th Number(整体二分)

    http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二 ...

  6. CDQ分治与整体二分小结

    前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完 ...

  7. BZOJ 1901 Zju2112 Dynamic Rankings ——整体二分

    [题目分析] 上次用树状数组套主席树做的,这次用整体二分去水. 把所有的查询的结果一起进行二分,思路很好. [代码] #include <cstdio> #include <cstr ...

  8. 洛谷P3527 MET-Meteors [POI2011] 整体二分

    正解:整体二分 解题报告: 传送门! 还有个双倍经验!(明明是一样的题目为什么你们一个紫一个黑啊喂! 这题首先要想到可以二分嘛,然后看到多组询问肯定就整体二分鸭 那就是基本套路啊,发现是区间修改单点查 ...

  9. 【BZOJ4009】[HNOI2015]接水果 DFS序+整体二分+扫描线+树状数组

    [BZOJ4009][HNOI2015]接水果 Description 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, ...

随机推荐

  1. SQL Server物化视图学习笔记

    一. 基本知识   摘抄自http://www.cnblogs.com/kissdodog/p/3385161.html SQL Server索引 - 索引(物化)视图 <第九篇> 索引视 ...

  2. Ubuntu10.04 python2.6下安装matplotlib环境

    一.准备工作1.sudo apt-get install python-numpy2.sudo apt-get install python2.6-dev3.sudo apt-get install ...

  3. EasyUI创建DataGrid及冻结列的两种方式

       第一种方式:通过HTML标签创建数据表格控件 <table class="easyui-datagrid" title="基本数据表格" style ...

  4. MVC请求管道

    下面是请求管道中的19个事件. (1)BeginRequest: 开始处理请求 (2)AuthenticateRequest授权验证请求,获取用户授权信息 (3):PostAuthenticateRe ...

  5. git使用遇到的坑

    把一个完整项目提交到github上步骤以及注意事项 Git的安装就不说了. 第一步:mkdir/cd 我们需要先创建一个本地的版本库(其实也就是一个文件夹). 你可以直接右击新建文件夹,也可以右击打开 ...

  6. createDocumentFragment()用法总结

    1.createDocumentFragment()方法,是用来创建一个虚拟的节点对象,或者说,是用来创建文档碎片节点.它可以包含各种类型的节点,在创建之初是空的. 2.DocumentFragmen ...

  7. subwoofer

    外文名:subwoofer 中文名:重低音音箱 俗    称:低音炮 归    类:音乐器材别    称:重低音音箱 低音炮是大家的一个俗称或者简称,严格讲应该是:重低音音箱.重低音其实是电子音乐里, ...

  8. Codeforce 835A - Key races

    Two boys decided to compete in text typing on the site "Key races". During the competition ...

  9. bzoj3196 二逼平衡树

    题目链接 平衡树系列最后一题 坑啊 10s时间限制跑了9764ms...还是要学一学bit套主席树啦... 经典的线段树套treap...至于第一发为什么要TLE(我不会告诉你treap插入的时候忘了 ...

  10. SQL8数据库定期自动备份

    我们知道,利用SQL Server 2008数据库可以实现数据库的定期自动备份.方法是用SQL SERVER 2008自带的维护计划创建一个计划对数据库进行备份, 下面我们将SQL SERVER 20 ...