傳送門(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;
}