[Luogu P3626] [APIO2009] 会议中心
题面
传送门:https://www.luogu.org/problemnew/show/P3626


Solution
如果题目只要求求出第一问,那这题显然就是大水题。
但是加上第二问的话.......那这题就成为大(du)火(liu)题了。
对于第一问:求一整个区间的最大线段总数,我们可以很轻松的切掉。
怎么处理第二问呢?
我们可以考虑这样做:
对于一条线段,如果它属于答案的一部分,那么它一定会有以下性质:
区间③的最大线段数 = 区间①的最大线段数 + 区间②的最大线段数 + 1(当前线段) (区间最大线段数指用传统贪心方法求出的一段区间的可能的最多的线段的数量)

那怎么求一段区间的最大线段数呢?
第一想法是前缀和?看起来很OK?
nope
因为不同区间中,里面的的初始线段会不同,以下这个图可以简单说明这种情况

但是,我们可以发现一个很重要的特点:
每条线段的下一条可行线段是固定的
有了这个特点,我们就可以对路径做倍增,就可以在log的时间求出某一个区间的线段数。
至于求每一个区间的第一条线段,我们可以用set+lowbound的方法找。
这样子,你就可以嘴巴AC这道题啦
实际上你会花费大量的时间来调这道毒瘤题
(我常数太大,开O2才能卡过(set太辣鸡))
Code
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<stack>
#include<cstring>
#include<vector>
using namespace std;
long long read()
{
long long x=0,f=1; char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
const int N=200000+100;
struct line
{
int l,r,no;
friend bool operator < (line A,line B)
{
return A.l<B.l;
}
}l[N];
bool cmp(line A,line B)
{
if(A.l==B.l)
{
if(A.r!=B.r)
return A.r>B.r;
else
return A.no>B.no;
}
return A.l<B.l;
}
bool cmp2(line A,line B)
{
return A.no<B.no;
}
int n,ans,root,fa[N][20+2];
bool use[N],vis[N];
stack <int> ms;
set <line> mset;
set <line> used;
vector <int> e[N];
void dfs(int now,int FA)
{
vis[now]=true;
fa[now][0]=FA;
for(int i=1;i<=20;i++)
fa[now][i]=fa[fa[now][i-1]][i-1];
for(int i=0;i<int(e[now].size());i++)
if(vis[e[now][i]]==false)
dfs(e[now][i],now);
}
int POW[21];
int Count(int L,int R)
{
line temp; temp.l=L;
set<line>:: iterator t=mset.lower_bound(temp);
if((*t).r > R) return 0;
int now=(*t).no,ans=1;
for(int i=20;i>=0;i--)
if(l[fa[now][i]].r<=R and fa[now][i]!=0)
now=fa[now][i],ans+=POW[i];
return ans;
}
int main()
{
//freopen("center.in","r",stdin);
//freopen("center.out","w",stdout); n=read();
for(int i=1;i<=n;i++)
l[i].l=read(),l[i].r=read(),l[i].no=i; sort(l+1,l+1+n,cmp);
memset(use,1,sizeof use);
for(int i=1;i<=n;i++)
{
while(ms.empty()==false and l[ms.top()].r>=l[i].r)
{
use[ms.top()]=false;
ms.pop();
}
ms.push(i);
}
int to=-1;
for(int i=1;i<=n;i++)
if(use[i]==true and l[i].l>to)
{
ans++;
to=l[i].r;
}
for(int i=1;i<=n;i++) e[i].reserve(4);
for(int i=1;i<=n;i++)
if(use[i]==true)
{
//cerr<<l[i].no<<" ";
mset.insert(l[i]);
bool OK=false;
for(int j=i+1;j<=n;j++)
if(use[j]==true and l[j].l>l[i].r)
{
e[l[j].no].push_back(l[i].no);
OK=true;
break;
}
if(OK==false)
e[0].push_back(l[i].no);
}
printf("%d\n",ans); dfs(0,0);
sort(l+1,l+1+n,cmp2);
for(int i=0;i<=20;i++)
POW[i]=1<<i;
l[0].r=0x3f3f3f3f;
line tt;
tt.l=-1,tt.r=-1,tt.no=0; mset.insert(tt),used.insert(tt);
tt.l=0x3f3f3f3f,tt.r=0x3f3f3f3f;mset.insert(tt),used.insert(tt);
for(int i=1;i<=n;i++)
{
int L,R;
set<line>:: iterator t=used.lower_bound(l[i]);
if((*t).l<=l[i].r) continue;
R=(*t).l-1;
t--;
if((*t).r>=l[i].l) continue;
L=(*t).r+1;
if(Count(L,l[i].l-1)+Count(l[i].r+1,R)==Count(L,R)-1)
{
printf("%d ",i);
used.insert(l[i]);
}
}
return 0;
}
[Luogu P3626] [APIO2009] 会议中心的更多相关文章
- Luogu 3626 [APIO2009]会议中心
很优美的解法. 推荐大佬博客 如果没有保证字典序最小这一个要求,这题就是一个水题了,但是要保证字典序最小,然后我就不会了…… 如果一条线段能放入一个区间$[l', r']$并且不影响最优答案,那么对于 ...
- P3626 [APIO2009]会议中心
传送门 好迷的思路-- 首先,如果只有第一问就是个贪心,排个序就行了 对于第二问,我们考虑这样的一种构造方式,每一次都判断加入一个区间是否会使答案变差,如果不会的话就将他加入别问我正确性我不会证 我们 ...
- 【题解】[APIO2009]会议中心
[题解][P3626 APIO2009]会议中心 真的是一道好题!!!刷新了我对倍增浅显的认识. 此题若没有第二份输出一个字典序的方案,就是一道\(sort+\)贪心,但是第二问使得我们要用另外的办法 ...
- [APIO2009]会议中心(贪心)
P3626 [APIO2009]会议中心 题目描述 Siruseri 政府建造了一座新的会议中心.许多公司对租借会议中心的会堂很 感兴趣,他们希望能够在里面举行会议. 对于一个客户而言,仅当在开会时能 ...
- [APIO2009]会议中心
[APIO2009]会议中心 题目大意: 原网址与样例戳我! 给定n个区间,询问以下问题: 1.最多能够选择多少个不相交的区间? 2.在第一问的基础上,输出字典序最小的方案. 数据范围:\(n \le ...
- BZOJ.1178.[APIO2009]会议中心(贪心 倍增)
BZOJ 洛谷 \(Description\) 给定\(n\)个区间\([L_i,R_i]\),要选出尽量多的区间,并满足它们互不相交.求最多能选出多少个的区间以及字典序最小的方案. \(n\leq2 ...
- BZOJ1178 APIO2009 会议中心 贪心、倍增
传送门 只有第一问就比较水了 每一次贪心地选择当前可以选择的所有线段中右端点最短的,排序之后扫一遍即可. 考虑第二问.按照编号从小到大考虑每一条线段是否能够被加入.假设当前选了一个区间集合\(T\), ...
- BZOJ1178或洛谷3626 [APIO2009]会议中心
BZOJ原题链接 洛谷原题链接 第一个问题是经典的最多不相交区间问题,用贪心即可解决. 主要问题是第二个,求最小字典序的方案. 我们可以尝试从\(1\to n\)扫一遍所有区间,按顺序对每一个不会使答 ...
- 【BZOJ】【1178】【APIO2009】convention会议中心
贪心 如果不考虑字典序的话,直接按右端点排序,能选就选,就可以算出ans…… 但是要算一个字典序最小的解就比较蛋疼了= = Orz了zyf的题解 就是按字典序从小到大依次枚举,在不改变答案的情况下,能 ...
随机推荐
- SQLSERVER如何在子查询中使用ORDER BY
今天在使用公司的一个pager接口的时候,需要传递一个查询的SQL语句,因为我希望他能够在pager对他查询出来的结果排序之前自己先进行排序, 于是在这个SQL中添加了ORDER BY,但是得到的结果 ...
- Python练习题 008:打印101-200之间的所有素数
[Python练习题 008]判断101-200之间有多少个素数,并输出所有素数. ---------------------------------------------------------- ...
- .net网站自动化部署-致两年前的遗留的问题
又到一年国庆,终于有了难得的几天空闲,计划陪陪媳妇娃子,再把最近阅读的几本相关书总结梳理下.当然,计划总是美好的,于时接到了一个老朋友电话.大意是他搞了一个.net小网站,部署了4个节点,每次更新程序 ...
- 1.入门篇十分钟了解Spring Cloud
文章目录 Spring Cloud入门系列汇总 为什么需要学习Spring Cloud 什么是Spring Cloud 设计目标与优缺点 设计目标 优缺点 Spring Cloud发展前景 整体架构 ...
- ansible-介绍
常用自动化运维工具 CFengine Chef Puppet 基于Ruby开发,采用C/S架构,扩展性强,基于SSL认证 SaltStack 基于python开发,采用C/S架构,相对于puppet更 ...
- git add 添加错文件如何撤销
git add 添加 多余文件 这样的错误是由于, 有的时候 可能 git add . (空格+ 点) 表示当前目录所有文件,不小心就会提交其他文件 git add 如果添加了错误的文件的话 以下是撤 ...
- thinkphp6.0.x 反序列化详记(一)
前言 这几天算是进阶到框架类漏洞的学习了,首当其冲想到是thinkphp,先拿thinkphp6.0.x来学习一下,体验一下寻找pop链的快乐. 在此感谢楷师傅的帮忙~ 环境配置 用composer指 ...
- linux网卡驱动程序架构
以cs89x0网卡驱动为例:
- 多测师讲解pthon_002字符,列表,元组,字段等
# # # 索引:# # # 正向索引: 0 1 2 3 4 5 6# # # l= a b c d e f g# # # 反向索引: -7 -6 -5 -4 ...
- 多测师讲解pythonl _字符,列表,元组,字典,集合,归纳_高级讲师肖sir