BZOJ1178 APIO2009 会议中心 贪心、倍增
只有第一问就比较水了
每一次贪心地选择当前可以选择的所有线段中右端点最短的,排序之后扫一遍即可。
考虑第二问。按照编号从小到大考虑每一条线段是否能够被加入。假设当前选了一个区间集合\(T\),当前正在考虑第\(i\)个区间\((l_i,r_i)\)能否被加入。假如集合\(T\)中存在一个区间\((l_j,r_j)\)与\((l_i,r_i)\)有交,那么显然这个区间不能被放进去。
如果说不存在这样的区间,考虑集合\(T\)中区间\((l_i,r_i)\)的前驱\((l_p,r_p)\)和后继\((l_q,r_q)\)。那么\((l_i,r_i)\)能够被加入的充要条件是:\(f(r_p+1,l_i-1) + f(r_i+1 , l_q - 1) + 1 = f(r_p + 1 , l_q - 1)\),其中\(f(i,j)\)表示只选择被包含于区间\([i,j]\)的区间,最多能够选择多少个区间。
显然我们不能每一次\(O(n)\)地计算\(f\)函数,考虑其他的方法。
在所有区间中,去掉包含了其他区间的区间。那么这些区间按照左端点排序后,左端点递增,右端点也递增。我们在这样的区间上贪心时,就可以对所有区间扫一遍,每一次能选即选。
注意到选中了某一个区间之后,下一次选中的区间是固定的,故考虑倍增优化。
设\(jmp_{i,j}\)表示从第\(i\)个区间开始向右选择,选择到的\(2^j\)个区间的编号,不难使用双指针预处理。
接下来考虑计算\(f(l,r)\)。通过二分查找找到左端点\(\geq l\)的最左端的区间\(x\),那么如果\(r_x \leq r\),\(x\)一定是会被选中的。而选中\(x\)之后接下来贪心选择的所有区间都可以通过倍增知道。那么倍增找到接下来选择的所有区间中右端点\(\leq r\)的最右段的区间并记录总共经过多少个区间即可。
对于区间集合\(T\)可以使用\(set\)维护。值得注意的是std::lower_bound在set等不支持随机访问的STL下复杂度不正确,请使用std::set::lower_bound。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<vector>
#include<cmath>
#include<random>
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
while(!isdigit(c))
c = getchar();
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return a;
}
const int MAXN = 2e5 + 3;
struct line{
int l , r , ind;
bool operator <(const line a)const{
return l < a.l || l == a.l && r < a.r;
}
}now[MAXN] , L[MAXN];
set < line > S;
set < line > :: iterator itS;
int N , cnt , cntL , jump[MAXN][21];
bool cmp(line a , line b){
return a.ind < b.ind;
}
void init(){
int minN = 1;
sort(now + 1 , now + N + 1);
for(int i = 1 ; i <= N ; ++i){
if(now[minN].r < now[i].l){
++cnt;
minN = i;
}
else
if(now[minN].r > now[i].r)
minN = i;
}
++cnt;
minN = 1e9;
for(int i = N ; i ; --i)
if(now[i].r < minN){
minN = now[i].r;
L[cntL++] = now[i];
}
reverse(L , L + cntL);
int p = cntL - 1;
for(int i = cntL - 1 ; i >= 0 ; --i){
while(p > i && L[p].l > L[i].r)
--p;
jump[i][0] = p == cntL - 1 ? -1 : p + 1;
for(int j = 1 ; j <= 19 ; ++j)
jump[i][j] = jump[i][j - 1] == -1 ? -1 : jump[jump[i][j - 1]][j - 1];
}
}
int query(int l , int r){
int t = lower_bound(L , L + cntL , (line){l , 0}) - L;
if(t == cntL || L[t].r > r)
return 0;
int sum = 0;
for(int i = 19 ; i >= 0 ; --i)
if(jump[t][i] != -1 && L[jump[t][i]].r <= r){
sum += 1 << i;
t = jump[t][i];
}
return sum + 1;
}
#define INF 2e9
int main(){
#ifndef ONLINE_JUDGE
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
N = read();
for(int i = 1 ; i <= N ; ++i){
now[i].l = read();
now[i].r = read();
now[i].ind = i;
}
init();
printf("%d\n" , cnt);
sort(now + 1 , now + N + 1 , cmp);
for(int i = 1 ; i <= N && cnt ; ++i){
int L , R;
itS = S.lower_bound(now[i]);
if(itS == S.end())
R = INF;
else
if(itS->l > now[i].r)
R = itS->l - 1;
else
continue;
if(itS == S.begin())
L = 0;
else
if((--itS)->r < now[i].l)
L = itS->r + 1;
else
continue;
if(query(L , now[i].l - 1) + query(now[i].r + 1 , R) + 1 == query(L , R)){
printf("%d " , i);
S.insert(now[i]);
--cnt;
}
}
return 0;
}
BZOJ1178 APIO2009 会议中心 贪心、倍增的更多相关文章
- BZOJ.1178.[APIO2009]会议中心(贪心 倍增)
BZOJ 洛谷 \(Description\) 给定\(n\)个区间\([L_i,R_i]\),要选出尽量多的区间,并满足它们互不相交.求最多能选出多少个的区间以及字典序最小的方案. \(n\leq2 ...
- [APIO2009]会议中心(贪心)
P3626 [APIO2009]会议中心 题目描述 Siruseri 政府建造了一座新的会议中心.许多公司对租借会议中心的会堂很 感兴趣,他们希望能够在里面举行会议. 对于一个客户而言,仅当在开会时能 ...
- [APIO2009]会议中心
[APIO2009]会议中心 题目大意: 原网址与样例戳我! 给定n个区间,询问以下问题: 1.最多能够选择多少个不相交的区间? 2.在第一问的基础上,输出字典序最小的方案. 数据范围:\(n \le ...
- 【题解】[APIO2009]会议中心
[题解][P3626 APIO2009]会议中心 真的是一道好题!!!刷新了我对倍增浅显的认识. 此题若没有第二份输出一个字典序的方案,就是一道\(sort+\)贪心,但是第二问使得我们要用另外的办法 ...
- BZOJ1178 [Apio2009]CONVENTION会议中心 贪心 set
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1178 题意概括 一堆线段,现在取出最多条数,使其互不覆盖,并输出字典序最小的方案. 题解 这题好坑 ...
- BZOJ1178或洛谷3626 [APIO2009]会议中心
BZOJ原题链接 洛谷原题链接 第一个问题是经典的最多不相交区间问题,用贪心即可解决. 主要问题是第二个,求最小字典序的方案. 我们可以尝试从\(1\to n\)扫一遍所有区间,按顺序对每一个不会使答 ...
- bzoj1178/luogu3626 会议中心 (倍增+STL::set)
贪心地,可以建出一棵树,每个区间对应一个点,它的父亲是它右边的.与它不相交的.右端点最小的区间. 为了方便,再加入一个[0,0]区间 于是就可以倍增来做出从某个区间开始,一直到某个右界,这之中最多能选 ...
- Luogu 3626 [APIO2009]会议中心
很优美的解法. 推荐大佬博客 如果没有保证字典序最小这一个要求,这题就是一个水题了,但是要保证字典序最小,然后我就不会了…… 如果一条线段能放入一个区间$[l', r']$并且不影响最优答案,那么对于 ...
- P3626 [APIO2009]会议中心
传送门 好迷的思路-- 首先,如果只有第一问就是个贪心,排个序就行了 对于第二问,我们考虑这样的一种构造方式,每一次都判断加入一个区间是否会使答案变差,如果不会的话就将他加入别问我正确性我不会证 我们 ...
随机推荐
- kvm 安装操作系统问题
1.出现error processing drive: 解决: --ram 设置到1024 2.分区的时候磁盘文件大小为0 解决:创建虚拟机的时候添加参数ormat=qcow2,size=7,bus= ...
- Android开发常用的一些功能列表(转)
文章来源:http://www.cnblogs.com/netsql/archive/2013/03/02/2939828.html 1.软件自动更新下载,并提示 2.软件登录注册,以及状态保存 3. ...
- 理解ES6中的Promise
一.Promise的作用 在ajax请求数据的过程中,我们可以异步拿到我们想要的数据,然后在回调中做相应的数据处理. 这样做看上去并没有什么麻烦,但是如果这个时候,我们还需要做另外一个ajax请求,这 ...
- python第十六天,昨天来晚了,作业终于完成了
作业 1: 员工信息表程序,实现增删改查操作 可进行模糊查询,语法至少支持下面3种: select name,age from staff_table where age > 22 select ...
- go xorm增删改查
1.增加 如果传入的bean不是完整的bean,并且传入了地址,则调用结束后会被赋值// InsertOne insert only one struct into database as a rec ...
- python 报错RuntimeError: dictionary changed size during iteration
a = {':0} for b in list(a.keys()): if a[b] == 0: del a[b] print(a) 报错是因为在字典迭代期间改变字典大小 我们可以通过取出字典的键值, ...
- 【数据结构】约瑟夫问题 C语言链表实现
1.首先,我们先来了解一下什么是约瑟夫环问题: 讲一个比较有意思的故事:约瑟夫是犹太军队的一个将军,在反抗罗马的起义中,他所率领的军队被击溃,只剩下残余的部队40余人,他们都是宁死不屈的人,所以不愿投 ...
- sql2008和sql2012混合安装后打开SQL Server 配置管理器查看出现“远程过程调用失败”0x800706be
sql2008和sql2012混合安装后打开SQL Server 配置管理器SQL Server服务出现“远程过程调用失败”0x800706be 网上很多人都说这个解决方案,通过卸载“Microsof ...
- wordpress使用rsync加openvpn进行同步和备份
目录 wordpress使用rsync加openvpn进行同步和备份 环境 普通同步策略的弊端 改良的同步方案 从头细说起 备份数据 打包wordpress项目和备份 服务端安装rsync 服务端配置 ...
- MySQL客户端工具的选择
最近因系统云化项目,学习使用MySQL集群,为了找一款顺手的mysql客户端,反复使用了多个工具,并筛选出一个自认为最满意的,在此分享. 先说我的选择:SQLyog. 尝试的客户端:Toad for ...