4301. 【NOIP2015模拟11.3】备用钥匙

64 篇文章 0 订阅
63 篇文章 0 订阅

Description

你知道Just Odd Inventions社吗?这个公司的业务是“只不过是奇妙的发明(Just Odd Inventions)”。这里简称为JOI社。
JOI社有N名员工,编号从1到N。所有员工的工作时间从时刻0持续到时刻M,时刻0和时刻M的时候,所有员工都必须在公司内。
某天,出于巧合,JOI社的每个员工都要出行恰好一次。员工i(1<=i<=N)在时刻Si离开公司,时刻Ti回到公司。同一时刻不会同时有两名以上的员工离开或回到公司。
JOI社的入口处有一扇巨大的门,员工只能通过这扇门离开或回到公司。门上挂着一把锁,从公司内部可以任意开锁或上锁,但从公司外部只有持有备用钥匙的人才能开锁或者上锁。时刻0时,锁是锁上的。
每个社员在回到公司的时候,都必须能够进入公司。换句话说,对于任意1<=i<=N,要么员工i持有备用钥匙,要么时刻Ti时门是开着的,否则是不被允许的。员工回到公司的时候,或者携带备用钥匙的员工离开公司的时候,可以选择锁门或不锁。没有携带备用钥匙的员工离开公司的时候没有办法锁门。
JOI社的社长决定把备用钥匙交给N个员工中的K个人。为了避免钥匙的丢失,员工之间不允许借用钥匙。此外,JOI社的社长很重视时间效率,因此每个员工在离开或回到公司的时刻以外,不允许开锁或者上锁。
出于安全的考虑,社长希望上锁的时间越长越好。现在他将员工出入公司的信息和准备交给员工的钥匙数量告诉了你,请你求出在能使所有员工回到公司的时候都能进入公司的大门的前提下,上锁的时间最长是多少。

Input

第一行三个空格分隔的整数N,M,K,表示JOI社的员工有N个,工作时间从时刻0到时刻M,备用钥匙有K把。
接下来N行,第i行(1<=i<=N)有两个空格分隔的整数Si,Ti,表示员工i在时刻Si离开公司,时刻Ti回到公司。

Output

输出一行一个正整数,表示上锁时间总和的最大值。

Sample Input

4 20 2
3 11
5 15
6 10
12 18

Sample Output

13
【HINT】
JOI社共有4名员工,工作时间为时刻0~时刻M,共有两把备用钥匙。
将钥匙交予员工2和员工4,一天日程如下:
时刻0,锁是关闭状态
时刻3,员工1离开公司。由于员工1没有备用钥匙,无法锁门。
时刻5,员工2离开公司,锁门。
时刻6,员工3离开公司。由于员工3没有备用钥匙,无法锁门。
时刻10,员工3回到公司,不锁门。
时刻11,员工1回到公司,锁门。
时刻12,员工4离开公司,锁门。
时刻15,员工2回到公司,锁门。
时刻18,员工4回到公司,锁门。
直到时刻20为止,锁都保持关闭状态。上锁的时间段为0~3,5~6,11~20,总计13时段,故答案为13。

Data Constraint

对于20%的数据,1<=N<=20,1<=M<=10^6
对于100%的数据:
1<=N<=2000
1<=M<=10^9
1<=K<N
0<Si<Ti<M (1<=i<=N)
对于任意i,j (1<=i<=N,1<=j<=N,i≠j),Si≠Sj,Si≠Tj,Ti≠Tj

Solution

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define val t[i].v-t[i-1].v
#define K(o) t[o].k
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define N 2010
using namespace std;
I n,m,k,x,y,p[N],c[N],d[N],to[N],cnt=0;
struct tim{I v,id,k;}t[N<<1];
I cmp(tim x,tim y){return x.v<y.v;}
ll ans=0,f[N][N],g[N][N],inf=1ll<<30,mx;
I main(){
	freopen("key.in","r",stdin);
	freopen("key.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	F(i,1,n){
		scanf("%d%d",&x,&y);
		t[i*2-1]=tim{x,i,1};
		t[i*2]=tim{y,i,2};
	}
	sort(t+1,t+1+n*2,cmp);
	ans=0;
	ans=t[1].v+m-t[n*2].v;
	F(i,2,n*2){
		if(K(i-1)==2&&K(i)==1) ans+=val;
		else if(K(i-1)==2&&K(i)==2) p[t[i].id]+=val;
		else if(K(i-1)==1&&K(i)==1) p[t[i-1].id]+=val;
		else{
			if(t[i-1].id==t[i].id) p[t[i].id]+=val;
			else c[to[t[i-1].id]=t[i].id]+=val;
		}
	}
	F(i,1,n) if(!c[i]){
		x=i;
		while(x){
			d[++cnt]=x;
			x=to[x];
		}
	}
	F(i,1,n){
		f[i][0]=-inf;
		F(j,1,min(k,i)){
			f[i][j]=max(f[i][j],f[i-1][j-1]+c[d[i]]+p[d[i]]);
			if(i>1) f[i][j]=max(f[i][j],g[i-2][j-1]+p[d[i]]);
			mx=max(mx,f[i][j]);
			g[i][j]=max(g[i][j],f[i][j]);
			g[i][j]=max(g[i][j],g[i-1][j]);
		}
	}
	printf("%lld\n",ans+mx);
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值