[POJ 3245] Sequence Partitioning [动态规划+线段树]

来源:互联网 发布:企业开淘宝网店的流程 编辑:程序博客网 时间:2024/06/07 06:20

已知一个序列,每个元素都是一个有序数对(A,B)。现在让你把这个序列分块,要求对于不同的块,前边的元素的B值必须大于后边的元素的A值,且每块的A值的最大值的和要小于等于Limit。问在这样的条件下,每块的B的和的最大值最小是多少?

首先我们发现,有些元素是必须处于一块儿的。当且仅当这个元素的前边的所有的B值大于这个元素的后边元素的所有的A值时,我们才可以把这个元素作为一块的最后一个元素。

对原序列进行处理,然后把必须处于一块儿的元素缩成一个元素,形成了一个新序列。然后我们就可以直接在新序列中求解,不用在意第一个约束条件了。

接着我们二分答案,然后仅需判断能否找到一组方案,每块的B的和都不超过ans,每块的a的最大值的和不超过Limit。

然后我们用dp判断是否有可行解,定义状态dp[i]表示前i个元素分块,每块的B不超过ans的前提下,每块的a的最大值的和最小是多少。

状态转移为:dp[i]=min{dp[j]+max{A_(j+1),A_(j+2),...,Ai}

然后用线段树维护这个dp数组,线段树中位置j的元素表示dp[j]+max{A_(j+1),A_(j+2),...,Ai的值,每次只需进行区间加法和区间最小值查询即可。

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MAXINT=~0u>>1;struct Node {Node *ls,*rs;long long v,add;void down() {if (add==0) return;if (ls) {ls->v+=add;ls->add+=add;}if (rs) {rs->v+=add;rs->add+=add;}add=0;}void repair() {v=min(ls->v,rs->v);}};Node segNode[100002],*bp,*root;int left,right;Node *makeTree(int l,int r) {Node *ans=bp++;if (l==r) {ans->v=ans->add=0;ans->ls=ans->rs=NULL;} else {int t=(l+r)/2;ans->ls=makeTree(l,t);ans->rs=makeTree(t+1,r);ans->v=ans->add=0;}return ans;}void clear(int l,int r) {bp=segNode;left=l;right=r;root=makeTree(l,r);}void set(int ll,int rr,long long x,Node *from=root,int l=left,int r=right) {if (l==ll&&r==rr) {from->add+=x;from->v+=x;} else {int t=(l+r)/2;from->down();if (rr<=t) set(ll,rr,x,from->ls,l,t);else if (ll>t) set(ll,rr,x,from->rs,t+1,r);else {set(ll,t,x,from->ls,l,t);set(t+1,rr,x,from->rs,t+1,r);}from->repair();}}long long get(int ll,int rr,Node *from=root,int l=left,int r=right) {if (l==ll&&r==rr) return from->v;int t=(l+r)/2;from->down();if (rr<=t) return get(ll,rr,from->ls,l,t);else if (ll>t) return get(ll,rr,from->rs,t+1,r);else return min(get(ll,t,from->ls,l,t),get(t+1,rr,from->rs,t+1,r));}int n,newn,limit;int a[50000];int b[50000];int maxa[50001];int minb[50000];bool isR[50000];int newa[50001];int newb[50001];int stka[50000];int stkl[50000];bool canGao(int ans,int a[],int b[],int n) {int sumb=0,left=1,p=0;long long dpi=0;clear(0,n);for (int i=1;i<=n;i++) {sumb+=b[i];while (sumb>ans) sumb-=b[left++];if (left>i) return false;set(i-1,i-1,(long long)dpi+a[i]);stkl[p]=i-1;while (p>0&&a[i]>=stka[p-1]) {set(stkl[p-1],stkl[p]-1,(long long)a[i]-stka[p-1]);p--;}stka[p++]=a[i];dpi=get(left-1,i-1);if (dpi>limit) return false;}return true;}int main() {int i;scanf("%d%d",&n,&limit);for (i=0;i<n;i++) scanf("%d%d",&a[i],&b[i]);minb[0]=b[0];for (i=1;i<n;i++) minb[i]=min(b[i],minb[i-1]);maxa[n]=-1;for (i=n-1;i>=0;i--) maxa[i]=max(a[i],maxa[i+1]);for (i=0;i<n;i++) if (minb[i]>maxa[i+1]) isR[i]=true;else isR[i]=false;memset(newa,-1,sizeof(newa));memset(newb,0,sizeof(newb));newn=1;for (i=0;i<n;i++) {newa[newn]=max(newa[newn],a[i]);newb[newn]+=b[i];if (isR[i]) newn++;}newn--;newa[0]=0; newb[0]=0;int l=0,r=MAXINT;while (l<r) {int t=((long long)l+r)/2;if (canGao(t,newa,newb,newn)) r=t;else l=t+1;}printf("%d\n",l);return 0;}


0 0
原创粉丝点击