BZOJ 1178 [Apio2009]CONVENTION会议中心【】

1178: [Apio2009]CONVENTION会议中心

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 1092   Solved: 429
[ 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
4 9
9 11
13 19
10 17

Sample Output

2
1 3

HINT

修复数据bug,并新加数据一组By NanoApe 2016.5.11


修复后数据:JudgeOnline/upload/201605/dd.rar









#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 400005

using namespace std;
int n,m,cnt,b[N],c[N],p[N],f[N][19],M;
struct T
{
	int l,r;
}a[N];
void mdy(int x,int k)
{
	int t=k*(m-x+1);
	for(int i=x;i<=m;i+=i&-i)
		b[i]+=t,c[i]+=k;
}
int qry(int x)
{
	int t1=0,t2=0;
	for(int i=x;i;i-=i&-i)
		t1+=b[i],t2+=c[i];
	return t1-t2*(m-x);
}
int kth(int x)
{
	int t=0,rk=0,tmp,bt=0,ct=0,tt;
	for(int i=M;i>=0;i--){
		tmp=t+(1<<i);
		if(tmp>m) continue;
		tt=rk+b[tmp]-c[tmp]*(m-tmp)+ct*(tmp-t);
		if(tt<x)
			rk=tt,t=tmp,bt+=b[tmp],ct+=c[tmp];
	}
	return t+1;
}
int find(int x)
{
	int l=1,r=m,mid;
	while(l<=r){
		mid=l+r>>1;
		if(p[mid]==x) return mid;
		if(p[mid]<x) l=mid+1;
		else r=mid-1;
	}
}
void Pre()
{
	sort(p+1,p+cnt+1);
	for(int i=1;i<=cnt;i++)
		if (i==1||p[i]!=p[m]) p[++m]=p[i];
	for(;1<<M+1<m;M++);
	for(int i=1;i<=n;i++){
		a[i].l=find(a[i].l);
		a[i].r=find(a[i].r);
	}
	memset(f,0x3f,sizeof f);
	for(int i=1;i<=cnt;i++)
		f[a[i].l][0]=min(f[a[i].l][0],a[i].r);
	for(int i=m-1;i;i--)
		f[i][0]=min(f[i][0],f[i+1][0]);
	for(int i=1;(1<<i)<m;i++)
		for(int j=1;j<=m;j++)
			if(f[j][i-1]<m)
				f[j][i]=f[f[j][i-1]+1][i-1];
}
int Get(int x,int y)
{
	int t=0;
	for (int i=M;i>=0;i--)
		if (f[x][i]<=y) t+=1<<i,x=f[x][i]+1;
	return t;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i].l);
		scanf("%d",&a[i].r);
		p[++cnt]=a[i].l;
		p[++cnt]=a[i].r;
	}
	Pre();
	int lych=Get(1,m),hz=0;
	printf("%d\n",lych);
	for(int i=1;i<=n;i++){
		int L=qry(a[i].l-1),R=qry(a[i].r);
		if (L!=R) continue;
		L=L?kth(L)+1:1;
		R=R!=qry(m)?kth(R+1)-1:m;
		if (Get(L,a[i].l-1)+Get(a[i].r+1,R)+1==Get(L,R)){
			hz++;
			printf("%d",i);
			if (hz!=lych) putchar(' ');
			mdy(a[i].l,1);
			mdy(a[i].r+1,-1);
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值