1178: [Apio2009]CONVENTION会议中心
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 824 Solved: 327
[ Submit][ Status][ Discuss]
Description
Siruseri政府建造了一座新的会议中心。许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议。 对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂。会议中心的销售主管认为:最好的策略应该是将会堂租借给尽可能多的客户。显然,有可能存在不止一种满足要求的策略。 例如下面的例子。总共有4个公司。他们对租借会堂发出了请求,并提出了他们所需占用会堂的起止日期(如下表所示)。 开始日期 结束日期 公司1 4 9 公司2 9 11 公司3 13 19 公司4 10 17 上例中,最多将会堂租借给两家公司。租借策略分别是租给公司1和公司3,或是公司2和公司3,也可以是公司1和公司4。注意会议中心一天最多租借给一个公司,所以公司1和公司2不能同时租借会议中心,因为他们在第九天重合了。 销售主管为了公平起见,决定按照如下的程序来确定选择何种租借策略:首先,将租借给客户数量最多的策略作为候选,将所有的公司按照他们发出请求的顺序编号。对于候选策略,将策略中的每家公司的编号按升序排列。最后,选出其中字典序最小1的候选策略作为最终的策略。 例中,会堂最终将被租借给公司1和公司3:3个候选策略是{(1,3),(2,3),(1,4)}。而在字典序中(1,3)<(1,4)<(2,3)。 你的任务是帮助销售主管确定应该将会堂租借给哪些公司。
Input
输入的第一行有一个整数N,表示发出租借会堂申请的公司的个数。第2到第N+1行每行有2个整数。第i+1行的整数表示第i家公司申请租借的起始和终止日期。对于每个公司的申请,起始日期为不小于1的整数,终止日期为不大于10^9的整数。N≤200000
Output
输出的第一行应有一个整数M,表示最多可以租借给多少家公司。第二行应列出M个数,表示最终将会堂租借给哪些公司。
Sample Input
4 9
9 11
13 19
10 17
Sample Output
1 3
HINT
Source
去掉第二个询问就是经典贪心,,,
现在问题是第二个要怎么处理???
回归第一问
我们去掉所有包含关系的区间中较大的那个,对结果一定不会有影响
因为如果某个最优方案有那个大的区间,那选小的一定没有差别
这样我们就得到一些小区间,这些区间的左右端点都是严格递增的
当我们从这些区间里选择最多的,两两不相交的区间,那就是第一问的答案了
贪心即可
考虑第二问,
对于一个区间,如果选择它,对最优答案没有影响,那显然这个区间是可以选的
这样按时间顺序考虑每个区间,每次检查这个区间选了会不会让答案变差,如果不会,就要它
如何检查??
显然,当前考虑的区间中,若有一个位置已经被覆盖过,那这个区间一定不能选
若全部为空,那这个区间一定包含于一个较大的空区间,记这个空区间为[i,j],记当前区间[l,r]
那么如果我们知道[i,j]、[i,l)以及(r,j]的最优答案,就能判断了
而之前已经处理出一些两两不互相包含的区间
将它们按照左端点升序排序
记Right[i]为第i个区间向右,第一个与它不相交的区间
对于[l,r],我们找出所有包含于[l,r]的区间,不断地沿Right数组走,就能找出答案
这走法可以用倍增算法优化成O(logn)
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
const int maxn = 4E5 + 10;
const int INF = ~0U>>1;
const int T = 16;
int n,cnt,top,tot,L[maxn],R[maxn],L2[maxn],R2[maxn],c[maxn*T]
,cur = 1,ans,Right[maxn][20],s[maxn],Num[maxn*2];
bool Mark[maxn*T];
void Read()
{
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%d%d",&L[i],&R[i]);
Num[++tot] = L[i];
Num[++tot] = R[i];
}
sort(Num + 1,Num + tot + 1);
for (int i = 2; i <= tot; i++)
if (Num[i] != Num[i-1])
Num[++cur] = Num[i];
Num[++cur] = INF;
L[0] = R[0] = cur;
for (int i = 1; i <= n; i++) {
L[i] = lower_bound(Num + 1,Num + cur + 1,L[i]) - Num;
R[i] = lower_bound(Num + 1,Num + cur + 1,R[i]) - Num;
if (R[R2[L[i]]] > R[i])
R2[L[i]] = i;
}
}
void Work()
{
for (int i = 1; i <= cur; i++) {
if (!R2[i]) continue;
while (top && R[s[top]] >= R[R2[i]]) --top;
s[++top] = R2[i];
}
for (int i = 1; i <= top; i++)
L2[i] = L[s[i]],R2[i] = R[s[i]];
int Now = top + 1;
L2[Now] = R2[Now] = cur;
for (int i = top; i; i--) {
while (L2[Now - 1] > R2[i]) --Now;
Right[i][0] = Now;
if (Right[i][0] > top) Right[i][0] = 0;
}
for (int j = 1; j < 20; j++)
for (int i = 1; i <= top; i++)
Right[i][j] = Right[Right[i][j-1]][j-1];
}
int Query(int l,int r)
{
if (l > r) return 0;
int pl = lower_bound(L2 + 1,L2 + top + 1,l) - L2;
int pr = lower_bound(R2 + 1,R2 + top + 1,r) - R2;
if (R2[pr] > r || pr > top) --pr;
if (pl > pr) return 0;
int pos = pl,ret = 1;
for (int j = 19; j >= 0; j--) {
if (!Right[pos][j]) continue;
if (Right[pos][j] <= pr) {
ret += (1<<j);
pos = Right[pos][j];
}
}
return ret;
}
void pushdown(int o,int l,int r)
{
if (Mark[o]) {
Mark[o<<1] = Mark[o<<1|1] = 1;
int mid = (l + r) >> 1;
c[o<<1] = mid - l + 1;
c[o<<1|1] = r - mid;
}
}
void Modify(int o,int l,int r,int ml,int mr)
{
if (ml <= l && r <= mr) {
Mark[o] = 1;
c[o] = r - l + 1;
return;
}
pushdown(o,l,r);
int mid = (l + r) >> 1;
if (ml <= mid) Modify(o<<1,l,mid,ml,mr);
if (mr > mid) Modify(o<<1|1,mid+1,r,ml,mr);
if (Mark[o<<1] && Mark[o<<1|1]) Mark[o] = 1;
c[o] = c[o<<1] + c[o<<1|1];
}
int Lower(int o,int l,int r,int ql,int qr)
{
int mid = (l + r) >> 1;
if (ql <= l && r <= qr) {
if (!c[o]) return cur;
if (Mark[o]) return l;
pushdown(o,l,r);
if (c[o<<1]) return Lower(o<<1,l,mid,ql,qr);
else return Lower(o<<1|1,mid + 1,r,ql,qr);
}
pushdown(o,l,r);
int ret = cur;
if (ql <= mid) ret = min(ret,Lower(o<<1,l,mid,ql,qr));
if (ret < cur) return ret;
if (qr > mid) ret = min(ret,Lower(o<<1|1,mid+1,r,ql,qr));
return ret;
}
int Upper(int o,int l,int r,int ql,int qr)
{
int mid = (l + r) >> 1;
if (ql <= l && r <= qr) {
if (!c[o]) return 0;
if (Mark[o]) return r;
pushdown(o,l,r);
if (c[o<<1|1]) return Upper(o<<1|1,mid+1,r,ql,qr);
else return Upper(o<<1,l,mid,ql,qr);
}
pushdown(o,l,r);
int ret = 0;
if (qr > mid) ret = max(ret,Upper(o<<1|1,mid+1,r,ql,qr));
if (ret) return ret;
if (ql <= mid) ret = max(ret,Upper(o<<1,l,mid,ql,qr));
return ret;
}
int Sum(int o,int l,int r,int ql,int qr)
{
if (ql <= l && r <= qr) return c[o];
pushdown(o,l,r);
int mid = (l + r) >> 1,ret = 0;
if (ql <= mid) ret += Sum(o<<1,l,mid,ql,qr);
if (qr > mid) ret += Sum(o<<1|1,mid+1,r,ql,qr);
return ret;
}
int main()
{
//freopen("1178.in","r",stdin);
//freopen("1178.out","w",stdout);
Read();
Work();
Modify(1,0,cur,0,0);
Modify(1,0,cur,cur,cur);
ans = Query(1,cur);
printf("%d\n",ans);
int K = 0;
for (int i = 1; i <= n; i++) {
if (Sum(1,0,cur,L[i],R[i]) > 0) continue;
int l = Upper(1,0,cur,0,L[i] - 1);
int r = Lower(1,0,cur,R[i] + 1,cur);
++l; --r;
int Need = Query(l,r);
int suml = Query(l,L[i] - 1);
int sumr = Query(R[i] + 1,r);
if (suml + sumr + 1 == Need) {
++K;
if (K < ans) printf("%d ",i);
else printf("%d",i);
Modify(1,0,cur,L[i],R[i]);
}
}
return 0;
}