题面

传送门: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] 会议中心的更多相关文章

  1. Luogu 3626 [APIO2009]会议中心

    很优美的解法. 推荐大佬博客 如果没有保证字典序最小这一个要求,这题就是一个水题了,但是要保证字典序最小,然后我就不会了…… 如果一条线段能放入一个区间$[l', r']$并且不影响最优答案,那么对于 ...

  2. P3626 [APIO2009]会议中心

    传送门 好迷的思路-- 首先,如果只有第一问就是个贪心,排个序就行了 对于第二问,我们考虑这样的一种构造方式,每一次都判断加入一个区间是否会使答案变差,如果不会的话就将他加入别问我正确性我不会证 我们 ...

  3. 【题解】[APIO2009]会议中心

    [题解][P3626 APIO2009]会议中心 真的是一道好题!!!刷新了我对倍增浅显的认识. 此题若没有第二份输出一个字典序的方案,就是一道\(sort+\)贪心,但是第二问使得我们要用另外的办法 ...

  4. [APIO2009]会议中心(贪心)

    P3626 [APIO2009]会议中心 题目描述 Siruseri 政府建造了一座新的会议中心.许多公司对租借会议中心的会堂很 感兴趣,他们希望能够在里面举行会议. 对于一个客户而言,仅当在开会时能 ...

  5. [APIO2009]会议中心

    [APIO2009]会议中心 题目大意: 原网址与样例戳我! 给定n个区间,询问以下问题: 1.最多能够选择多少个不相交的区间? 2.在第一问的基础上,输出字典序最小的方案. 数据范围:\(n \le ...

  6. BZOJ.1178.[APIO2009]会议中心(贪心 倍增)

    BZOJ 洛谷 \(Description\) 给定\(n\)个区间\([L_i,R_i]\),要选出尽量多的区间,并满足它们互不相交.求最多能选出多少个的区间以及字典序最小的方案. \(n\leq2 ...

  7. BZOJ1178 APIO2009 会议中心 贪心、倍增

    传送门 只有第一问就比较水了 每一次贪心地选择当前可以选择的所有线段中右端点最短的,排序之后扫一遍即可. 考虑第二问.按照编号从小到大考虑每一条线段是否能够被加入.假设当前选了一个区间集合\(T\), ...

  8. BZOJ1178或洛谷3626 [APIO2009]会议中心

    BZOJ原题链接 洛谷原题链接 第一个问题是经典的最多不相交区间问题,用贪心即可解决. 主要问题是第二个,求最小字典序的方案. 我们可以尝试从\(1\to n\)扫一遍所有区间,按顺序对每一个不会使答 ...

  9. 【BZOJ】【1178】【APIO2009】convention会议中心

    贪心 如果不考虑字典序的话,直接按右端点排序,能选就选,就可以算出ans…… 但是要算一个字典序最小的解就比较蛋疼了= = Orz了zyf的题解 就是按字典序从小到大依次枚举,在不改变答案的情况下,能 ...

随机推荐

  1. MySQL 5.7 InnoDB锁

    简介 参考https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-gap-locks. InnoDB引擎实现了标准的行级别 ...

  2. 摄像头Sensor 图像格式

    以0V7725为例: 顺便介绍一下0V7725的主要管脚,管脚定义能体现功能,体现使用方法.

  3. JavaScript函数报错SyntaxError: expected expression, got ';'

    故事背景:编写Javaweb项目,在火狐浏览器下运行时firebug报错SyntaxError: expected expression, got ';'或者SyntaxError: expected ...

  4. 固件(Firmware)

    来源:https://baike.baidu.com/item/%E5%9B%BA%E4%BB%B6/627829 固件   固件(Firmware)就是写入EPROM(可擦写可编程只读存储器)或EE ...

  5. Eclipse 重命名工程、包、类

    Eclipse版本 重命名工程,使用鼠标右键点击工程,选Refactor > Rename...(快捷键:Alt + Shift + R) 重命名包.类的操作与重命名工程一样. 其实,最简单的操 ...

  6. JVM内存布局(又叫Java运行时数据区)

    JVM 堆中的数据是共享的,是占用内存最大的一块区域. 可以执行字节码的模块叫作执行引擎. 执行引擎在线程切换时怎么恢复?依靠的就是程序计数器. JVM 的内存划分与多线程是息息相关的.像我们程序中运 ...

  7. Centos7系统下Docker开启认证的远程端口2376配置教程

    docker开启2375会存在安全漏洞 暴露了2375端口的Docker主机.因为没有任何加密和认证过程,知道了主机IP以后,,任何人都可以管理这台主机上的容器和镜像,以前贪图方便,只开启了没有认证的 ...

  8. git 上传文件到 gitee 码云远程仓库(强制上传)

    1.先git init 会出现一个.git的文件夹,有些人可能是隐藏了,工具哪里打开就行了 2.将当前的数据上传到码云,看清楚奥,是当前.git add ./ 这是代表当前的意思 3.将上传的数据备注 ...

  9. 在linux下搭建l2tp隧道

    搭一个l2tp隧道,拓扑如下 两台机器是CentOS5,内核选上CONFIG_LEGACY_PTYS选项后自己编译的,l2tp是已经停更的l2tpd-0.69.先在LS上配置IP地址,iptables ...

  10. 真的有这么丝滑吗?近日国外一小哥深入研究了KMP算法……

    近日被朋友问到了字符串匹配算法,让我想起了大二上学期在一次校级编程竞赛中我碰到同样的问题时,为自己写出了暴力匹配算法而沾沾自喜的经历. 现在想来,着实有点羞愧,于是埋头去学习了一下KMP算法,为了让自 ...