题目大意:给你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;
}
浙公网安备 33010602011771号