您的位置:首页 > 其它

SPOJ1748 - SEQPAR2 二分答案 DP优化 单调队列+线段树

2014-04-09 15:15 507 查看
 题目太蛋疼。。

首先有一个N^2LIMIT的最最暴力的DP。囧

然后可以换个DP的东西,二分答案,然后DP验证是否可行。(f
<=LIMIT)

对于第一个条件,其实可以与处理出来最小的必须在一起的块。然后合并起来。然后第一个条件就没了。

然后就要考虑第二个条件了。

还是二分答案,DP验证 。但是N^2的暴力DP肯定要TLE的。

考虑优化一下dp.

转移是:

f[i]=Min{f[j]+MaxA(j+1,i)}  sumb(j+1,i)≤X (x是当前二分的答案)

然后这玩意儿竟然可以优化。

MaxA(j+1,i)是随j变大单调不增的。

然后考虑以A为关键字建一个单调递减的栈。

然后用线段树维护f[j]+MaxA(j+1,i) 整个的值

但是修改的只是MaxA(j+1,i)

MaxA(j+1,i)是成段变化的,每次加上一个差值就可以了。

但是f[j]+MaxA(j+1,i) 是不单调的。所以要在线段树里面询问最小值

然后修改一段区间是logN的 ,每个点最多单独更新一次。每次DP是NlogN的。

下面是错误的代码。。。(为啥还放上来 。。)

#include <cstdio>
#include <algorithm>
#define per(i,r,l) for (int i=r;i>=l;--i)
#define rep(i,l,r) for (int i=l;i<=r;++i)
typedef unsigned int UI;
bool upmax(int &a,int b){return a<b?a=b,1:0;}
bool upmax(UI &a,UI b){return a<b?a=b,1:0;}
UI Max(UI a,UI b){return a<b?b:a;}
const int MAX_N=50050;
UI n,lim;
UI a[MAX_N],b[MAX_N];
UI sum[MAX_N];
void New(){
static UI max[MAX_N];
per(i,n,1) max[n-i+1]=Max(max[n-i],a[i]);
static int L[MAX_N],R[MAX_N],top=0;
bool fir=true;int r=0;
rep(i,1,n){
upmax(r,n-(std::lower_bound(max,max+n-i+1,b[i])-max)+1);
if (fir) L[++top]=i,fir=0;
if (i==r) R[top]=i,fir=1;
}
n=top;
rep(i,1,top){
UI s=0,mx=0;
rep(j,L[i],R[i]) upmax(mx,a[j]),s+=b[j];
a[i]=mx,b[i]=s;
sum[i]=sum[i-1]+b[i];
}
}
int getp(UI *a,int l,int r,int v){
if (v<a[l]) return l;
while (r-l>1){
int mid=l+r>>1;
if (v>a[mid]) r=mid;
else l=mid;
}
return r;
}
bool check(UI x){
static UI f[MAX_N],que1[MAX_N],que2[MAX_N];
int h1=0,t2=0,h2=0;
que2[t2]=0;f[0]=0;
rep(i,1,n){
while(0<h1&&a[que1[h1]]<=a[i]) h1--;
que1[++h1]=i;
while(t2<h2&&((sum[i]-sum[que2[t2]])>x)) t2++;
f[i]=f[que2[t2]]+a[getp(que1,1,h1,que2[h2])];
//printf("%d\n",f[i]);
while(t2<h2&&(   f[que2[h2]]+a[getp(que1,1,h1,que2[h2])]
>f[i]   +    a[getp(que1,1,h1,i)])) h2--;
que2[++h2]=i;
}
return f
<=lim;
}
void Solve(){
UI l=0,r=1;
rep(i,1,n) upmax(l,b[i]-1),r+=b[i];
while (r-l>1){
UI mid=l+r>>1;
if (check(mid)) r=mid;
else l=mid;
}
printf("%d\n",r);
}
int main(){
freopen("partition.in","r",stdin);
freopen("partition.out","w",stdout);
scanf("%u%u",&n,&lim);
rep(i,1,n) scanf("%u%u",&a[i],&b[i]);
New();
//rep(i,1,n) printf("%d %d\n",a[i],b[i]);
//check(9);
Solve();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: