首先我们有朴素dp
表示把前i个分成k段的最小代价
其中
表示把i~j的贞鱼分在一辆车会产生的代价,可以预处理前缀和得到。(把矩阵中i>j的位置置为0,这样每次求的就是一个若干行的前缀的和)
这样复杂度
我们可以按k分层转移,
我们考虑k1< k2,如果某个时刻k2比k1优了,那么k2将永远比k1优。因为
不等式右边的那个式子是随着i的增大而增大的。
然而这个式子不能斜率优化!因为他的右边还是不只跟i有关,在换了决策点之后,他不一定是单增的!
因此我们只好对于每个k1,k2去二分算一个k2优于k1的最早时间i。
然后可以用一个单调队列来维护这些决策点,保证k2优于k1的最早时间单增。
复杂度
还是过不去!
我们发现f[n][k]随着k是下凸的。
因此我们可以wqs二分!
复杂度
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 4010
inline char gc(){
static char buf[1<<16],*S,*T;
if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int n,K,s[N][N],f[N],w[N],ans=0,q[N];
inline int calc(int j,int i){
return f[j]+s[i][i]-s[j][i];
}
inline bool better(int k1,int k2,int i){
int val1=calc(k1,i),val2=calc(k2,i);
if(val2<val1) return 1;
if(val1<val2) return 0;
return w[k2]<=w[k1];
}
inline int bettert(int k1,int k2){
int l=k2+1,r=n;
while(l<=r){
int mid=l+r>>1;
if(better(k1,k2,mid)) r=mid-1;
else l=mid+1;
}return r+1;
}
inline int jud(int mid){
int qh=1,qt=0;q[++qt]=0;
for(int i=1;i<=n;++i){
while(qh<qt&&better(q[qh],q[qh+1],i)) ++qh;
f[i]=calc(q[qh],i)+mid,w[i]=w[q[qh]]+1;
while(qh<qt&&bettert(q[qt-1],q[qt])>bettert(q[qt],i)) --qt;q[++qt]=i;
}return w[n];
}
int main(){
// freopen("a.in","r",stdin);
n=read();K=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j) s[i][j]=read();
for(int i=1;i<=n;++i)
for(int j=1;j<i;++j) s[i][j]=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j) s[i][j]=s[i][j-1]+s[i][j];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j) s[i][j]=s[i-1][j]+s[i][j];
int l=0,r=s[n][n];
while(l<=r){
int mid=l+r>>1;
if(jud(mid)<=K) r=mid-1,ans=f[n]-K*mid;
else l=mid+1;
}printf("%d\n",ans);
return 0;
}