题目大意:给你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; }