傳送門
解析:
優秀的斜率優化DP。
首先DP式子不是很好想,再加上本蒟蒻很久沒寫斜率優化,這道題就場上直接咕咕咕了。。。
思路:
不要想一次四個方向處理完(不然下場和我一樣,無法處理後效性),我們可以處理四次,分別處理左上右下右上左下,最後所有答案取一個就可以了。
那麼考慮怎麼處理左上方向,其他的顯然可以通過對稱轉變一下。
考慮我們已經處理到第列,我們可以維護前行每一行平移過來最近的。第行記爲,所以我們的決策就是
這個東西可以斜率優化一下,即
考慮斜率優化,另若是較優決策則有
即
然後斜率優化亂搞就行了。
然後發現是單調的,所以可以直接來單調隊列維護下凸殼就行了。
代碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
cs int N=1003;
int n,m;
bool way[N][N];
int ans[N][N];
int f[N][N];
int last1[N];
struct Point{
int x,y;
Point(cs int &_x=0,cs int &_y=0):x(_x),y(_y){}
friend ll operator*(cs Point &a,cs Point &b){return 1ll*a.x*b.y-1ll*a.y*b.x;}
friend Point operator-(cs Point &a,cs Point &b){
return Point(a.x-b.x,a.y-b.y);
}
}p[N];
inline void solve(){
fill(last1+1,last1+m+1,N<<1);
for(int re i=1;i<=n;++i){
int head=1,tail=0;
for(int re j=1;j<=m;++j){
++last1[j];
if(way[i][j])last1[j]=0;
Point tmp=Point(j,last1[j]*last1[j]+j*j);
while(head<tail&&(p[tail]-p[tail-1])*(tmp-p[tail-1])<=0)--tail;
p[++tail]=tmp;
while(head<tail&&p[head].y-2*j*p[head].x>p[head+1].y-2*j*p[head+1].x)++head;
f[i][j]=p[head].y-2*j*p[head].x+j*j;
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
for(int re i=1;i<=n;++i){
string sss;
cin>>sss;
for(int re j=1;j<=m;++j)way[i][j]=sss[j-1]^48;
}
solve();
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)ans[i][j]=f[i][j];
for(int re i=1;i<=n;++i)reverse(way[i]+1,way[i]+m+1);
solve();
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)ans[i][j]=min(ans[i][j],f[i][m-j+1]);
for(int re i=1;i*2<=n;++i)swap(way[i],way[n-i+1]);
//交換數組的swap和交換變量的swap不是同一個,有不同的聲明和實現。
solve();
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)ans[i][j]=min(ans[i][j],f[n-i+1][m-j+1]);
for(int re i=1;i<=n;++i)reverse(way[i]+1,way[i]+m+1);
solve();
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)ans[i][j]=min(ans[i][j],f[n-i+1][j]);
for(int re i=1;i<=n;++i){
for(int re j=1;j<=m;++j)
printf("%d ",ans[i][j]);
pc('\n');
}
return 0;
}