BZOJ5424 烧桥计划(单调队列优化dp)
传送门(vjudge)
解题思路
注意到 \(a_i\) 的范围很小,是1000~2000之间,于是我们可以直观感受到k一定不会特别大,推一下可以得出 k 最多大概在四五百左右,于是可以直接考虑 dp[i][j] 为前 i 个数里面选了 j 个分割点,且第 i 个数是分割点的最小代价。
转移要分两种情况讨论:
- sum[pre+1~i-1] 大于 m
- sum[pre+1~i-1] 小于等于 m
可以用类似双指针的东西来维护这个分割点。
随着 i 的增大,分割点右移,会有第二种情况的点变成第一种情况。
第一种情况的点集可以只维护一个min值,不断更新即可。
第二种情况的点集一开始用想用优先队列加个log梭哈过去,结果发现90分,很难受,只能被迫改成单调队列qwq(比较明显,要是pre比你靠后并且dp值还比你小,那你就可以退役了)
AC代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while((c<'0'||c>'9')) {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
#define pii pair<int,int>
#define mp(a,b) make_pair(-a,b)
#define fi first
#define se second
const int maxk=305;
const int maxn=1e5+5;
deque<int> q[maxk];
int dp[maxn][maxk];
int l[maxk],a[maxn],d[maxn],minn[maxk];
int main(){
int T=1;
while(T--){
int n=read(),m=read();
for(int i=1;i<=n;i++){
a[i]=read();
d[i]=d[i-1]+a[i];
}
memset(l,0,sizeof(l));
memset(minn,0,sizeof(minn));
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=0;i<=300;i++) while(!q[i].empty()) q[i].pop_front();
q[0].push_back(0);
for(int i=1;i<=n;i++){
for(int j=min(i,300);j>=2;j--){
int lst=j-1;
while(d[i-1]-d[l[lst]]>m){
l[lst]++;
if(dp[minn[lst]][lst]+d[i-1]-d[minn[lst]]>dp[l[lst]][lst]+d[i-1]-d[l[lst]]) minn[lst]=l[lst];
}
while(!q[lst].empty()&&q[lst].front()<l[lst]){
q[lst].pop_front();
}
int res=dp[minn[lst]][lst]+d[i-1]-d[minn[lst]];
if(!q[lst].empty()){
res=min(res,dp[q[lst].front()][lst]);
}
dp[i][j]=res+j*a[i];
while(!q[j].empty()&&dp[i][j]<dp[q[j].back()][j]) q[j].pop_back();
q[j].push_back(i);
}
dp[i][0]=(d[i]>m?d[i]:0);
dp[i][1]=(d[i-1]>m?d[i-1]:0)+a[i];
while(!q[1].empty()&&dp[i][1]<dp[q[1].back()][1]) q[1].pop_back();
q[1].push_back(i);
}
int ans=(d[n]>m?d[n]:0);
for(int i=1;i<=n;i++){
for(int j=1;j<=min(i,300);j++){
ans=min(ans,dp[i][j]+(d[n]-d[i]>m?d[n]-d[i]:0));
}
}
printf("%d\n",ans);
}
return 0;
}