[Luogu1848][USACO12OPEN]书架Bookshelf DP+set+决策单调性

题目链接:https://www.luogu.org/problem/show?pid=1848

题目要求书必须按顺序放,其实就是要求是连续的一段。于是就有DP方程$$f[i]=min{f[j]+max{h_k}}$$其中的k以及j的关系应该满足$$sum_{k=j+1}^iw_k<=L$$

这样是$O(n^2)$的肯定不行。发现对于一个$h[i]$到前一个比它大的$h[j]$之间,都被$h[i]$所影响这,且这些影响某一段区间的关键点是单调下降的,同时发现$f[j]$总不会比$f[j+1]$更劣,证明显然。

于是我们只需要维护这样一个关键点集就可以从中取得最优答案。观察DP方程中的条件限制,发现前面的点可能会被从前往后舍去,而后面的点可以把之前的点给覆盖掉,想到用单调队列维护。

队首元素的删除用$w$的区间和来维护,队尾元素则直接用新加入的$h[i]$不断合并掉就好了。

然后对于每一个当前合法的关键点,我们都需要记录其对应的答案,每次取最小值。不仅需要插入,同时还需要从中删除掉不合法的答案。我们可以用双堆或者平衡树来做,当然set也是可以的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<set>
 5 using namespace std;
 6 typedef long long ll;
 7 int inline readint(){
 8     int Num;char ch;
 9     while((ch=getchar())<'0'||ch>'9');Num=ch-'0';
10     while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0';
11     return Num;
12 }
13 int n,m;
14 int h[100010],w[100010],la;
15 ll f[100010],sum;
16 int q[100010],head,tail;
17 bool in[100010];
18 multiset <ll> s;
19 //  f[i]=f[j]+max(h[k])  sigma(w[k])<=L  k:i->j+1
20 int main(){
21     n=readint();
22     m=readint();
23     la=head=q[0]=1;
24     tail=sum=0;
25     in[1]=true;
26     for(int i=1;i<=n;i++){
27         h[i]=readint();
28         w[i]=readint();
29         sum+=w[i];
30         while(sum>m){
31             if(in[la]){
32                 s.erase(f[la-1]+h[la]);
33                 in[la]=false;
34                 head++;
35             }
36             else h[la]=h[la-1];
37             sum-=w[la++];
38         }
39         if(!in[la]){
40             h[la]=h[la-1];
41             s.insert(f[la-1]+h[la]);
42             q[--head]=la;
43             in[la]=true;
44         }
45         q[++tail]=i;
46         s.insert(f[i-1]+h[i]);
47         in[i]=true;
48         while(head<=tail&&h[q[tail]]<=h[i]){
49             s.erase(f[q[tail]-1]+h[q[tail]]);
50             in[q[tail]]=false;
51             tail--;
52         }
53         h[q[++tail]]=h[i];
54         s.insert(f[q[tail]-1]+h[q[tail]]);
55         in[q[tail]]=true;
56         f[i]=*s.begin();
57     }
58     printf("%lld
",f[n]);
59     return 0;
60 }
原文地址:https://www.cnblogs.com/halfrot/p/7491879.html