YY_More

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

题目大意:给你N个有序数对(Ai, Bi),以及一个限制Limit。要求你把这些数对分成若干个连续区间,使得下面的两个条件得到满足:

一、对于任意的p < q,如果p与q属于不同的区间,有Bp > Aq。

二、设Mi为第i个区间中最大的A值。满足∑(Mi, 1≤i≤P)≤Limit,其中P是区间总数。

设Si是第i个区间中所有B的和。求满足上面条件的所有划分方案中,能够得到的最小的min(Si, 1≤i≤P)是多少。

对于任意的p < q,如果p与q属于不同的区间,有Bp > Aq。换句话说就是如果p<q且Bp<Aq则p,q必在同一个区间。

那么我们把必须在一起的都捆到一起,让区间里最大的A[i]作为区间的A[i],区间里B[i]的和作为区间的B[i]。

这样我们发现这道题的模型其实和上一道(POJ 3017)是一样的。但是那道题已知的区间和上界在这道题里是要求的答案。

直接求实在是没有办法,我们只好二分答案了,这样就和上道题一模一样了。POJ 3017里我的程序中set的使用有点bug,这里已经更正了。

这道题确实是神题。。。累死我了。

//By YY_More
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
multiset<int> tree;
int D[100010],SA[100010],SB[100010],point,temp,N,Limit,num,A[100010],B[100010],match[100010],EA[100010],EB[100010],f[100010];
bool cmpa(int x,int y){return A[x]<A[y];}
bool cmpb(int x,int y){return B[x]<B[y];}
bool make(int M){
	tree.clear();
	int low=1,sum=0,L=0,R=-1;
	for (int i=1;i<=num;i++){
		sum+=EB[i];
		while (sum>M){
			sum-=EB[low++];
		}	
		if (low>i) return false;
		while (L<=R&&EA[D[R]]<=EA[i]){
			if (R>L) tree.erase(tree.find(f[D[R-1]]+EA[D[R]]));
			R--;
		}
		D[++R]=i;
		if (R>L) tree.insert(f[D[R-1]]+EA[D[R]]);
		while (D[L]<low){
			tree.erase(tree.find(f[D[L]]+EA[D[L+1]]));
			L++;
		}
		f[i]=f[low-1]+EA[D[L]];
		if (L<R) f[i]=min(*tree.begin(),f[i]);
	}		
	return f[num]<=Limit;
};	
int main(){
	scanf("%d%d",&N,&Limit);
	for (int i=1;i<=N;i++){
		scanf("%d%d",&A[i],&B[i]);
		SA[i]=SB[i]=i;
	}
	sort(SA+1,SA+N+1,cmpa);
	sort(SB+1,SB+N+1,cmpb);
	point=N;
	for (int i=N;i>0;i--){
		while (B[SB[i]]<=A[SA[point]]) temp=max(temp,SA[point--]);
		match[SB[i]]=max(SB[i],temp);
	}
	temp=0;
	int okA=0,okB=0;
	for (int i=1;i<=N;i++){
		temp=max(temp,match[i]);
		okA=max(okA,A[i]);
		okB+=B[i];
		if (temp==i){
			EA[++num]=okA;
			EB[num]=okB;
			okA=okB=0;
		}
	}
	int down=1,up=(1<<31)-2;
	while (down<up)
		if (make((down+up)/2)) up=(down+up)/2;
			else down=((down+up)/2)+1;
	printf("%d\n",down);
	return 0;
}
posted on 2011-06-29 15:35  YY_More  阅读(386)  评论(0编辑  收藏  举报