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