bzoj2678[Usaco2012 Open]Bookshelf*

bzoj2678[Usaco2012 Open]Bookshelf

题意:

给出一个序列,有两个元素ai、bi,要求将一个序列分成几段,每段的bi和不能超过l,每段的代价为该段最大的ai,求一个方案使代价和最小。n≤100000。

题解:

首先方程为f[i]=f[j]+mx[j+1..i],sum[i]-sum[j]<=l。但这样会T。令g[j]=mx[j+1..i],则g[j]关于j单调不上升,故每次转移时bi只会修改到前面几个g[j]。所以可以把相同的g[j]合并为一块,记录这一块的头节点和尾节点,用一个set维护f[j]+g[j]和一个队列维护所有的块,每次看队列头的块的头节点是否不满足sum[i]-sum[j]<=l,如果不满足则删头结点,在删头节点时如果把尾节点也删了说明整个块都被删,出队列,接着用bi去更新队尾的比它小的g[j]块,将它们合并,而f[i]就是当前set里面的最小值,注意要开long long。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <set>
 5 #define inc(i,j,k) for(int i=j;i<=k;i++)
 6 #define maxn 100010
 7 #define ll long long
 8 using namespace std;
 9 
10 inline int read(){
11     char ch=getchar(); int f=1,x=0;
12     while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
13     while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
14     return f*x;
15 }
16 multiset<ll>st;
17 int n,l,h[maxn],ql,qr; ll sm[maxn],f[maxn]; struct nd{int f,t;}q[maxn];
18 int main(){
19     n=read(); l=read(); ql=1; qr=0;
20     inc(i,1,n){
21         h[i]=read(); int w=read(); sm[i]=sm[i-1]+w; int x=i;
22         while(ql<=qr&&h[i]>=h[q[qr].t]){
23             st.erase(st.find(f[q[qr].f-1]+h[q[qr].t])); x=q[qr].f; qr--;
24         }
25         q[++qr]=(nd){x,i}; st.insert(f[q[qr].f-1]+h[i]);
26         while(ql<=qr&&sm[i]-sm[q[ql].f-1]>l){
27             st.erase(st.find(f[q[ql].f-1]+h[q[ql].t])); q[ql].f++;
28             if(q[ql].f>q[ql].t)ql++;else st.insert(f[q[ql].f-1]+h[q[ql].t]);
29         }
30         f[i]=*st.begin();
31     }
32     printf("%lld",f[n]); return 0;
33 }

 

20161114

posted @ 2016-11-15 21:14  YuanZiming  阅读(217)  评论(0编辑  收藏  举报