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]会议中心
传送门 好迷的思路-- 首先,如果只有第一问就是个贪心,排个序就行了 对于第二问,我们考虑这样的一种构造方式,每一次都判断加入一个区间是否会使答案变差,如果不会的话就将他加入别问我正确性我不会证 我们 ...
随机推荐
- CSS盒模型的介绍
CSS盒模型的概念与分类 CSS盒模型就是一个盒子,封装周围的HTML元素,它包括内容content.边框border.内边距padding.外边距margin. CSS盒模型分为标准模型和 ...
- css中那些属性可以被继承
主要的有: 字体相关:line-height, font-family, font-size, font-style, font-variant, font-weight, font 文本相关: le ...
- hosts 文件
各系统平台hosts文件存放路径 路径如下: Windows系统: C:\Windows\System32\drivers\etc\hosts Linux系统:/etc/hosts ...
- VUE 配置vue-devtools调试工具
1. 通过 Git 克隆项目到本地 git clone https://github.com/vuejs/vue-devtools.git 2. Git 进入到 vue-devtools 所在目录,然 ...
- Stackoverflow 珠玑:C#封装重试指定次数的功能
最近写的一个 .NET Core 爬虫里用到了需要多次重试的功能,本着无脑输出的精神,google 了一下,还真给我找到了: public static T Retry<T, TExceptio ...
- (网页)AngularJS中【Error: [$rootScope:inprog]】的解决办法(转)
转自CSDN: Error: [$rootScope:inprog] http://errors.angularjs.org/1.5.8/$rootScope/inprog?p0=%24apply 如 ...
- JavaScript变量提升的理解
变量提升 先说三句总结性的话: let 的「创建」过程被提升了,但是初始化没有提升. var 的「创建」和「初始化」都被提升了. function 的「创建」「初始化」和「赋值」都被提升了. 所以,我 ...
- Ubuntu18.04 更换源
在虚拟机新建一个Ubuntu18.04.1-live-server-amd64当做服务器 在安装软件时报错: slave@slave:~$ sudo -s[sudo] password for sla ...
- Mysqli面向对象操作数据库
Mysqli面向对象操作数据库 首先配置一下数据库: 接着用PHP中的Mysqli扩展库面向对象查询这个数据表. 操作分为以下几个步骤: 连接数据库 操作数据库 处理结果 关闭资源 <?php ...
- map的使用注意事项
map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取 map的长度是不固定的,也就是和slice一样,也是一种引用类型 内置的len函数同样适用于map,返回 ...