题解 P7274 草地
Orz EA!
首先你发现,向上流和向下流实际上是一样的,左右同理,并且操作顺序是没有关系的,所以一定存在一种最优策略是下移
考虑两个黑点
一种求法是枚举
黑点数较多时
可能会卡常,但显而易见这个
这份代码是
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a,i##i=b;i<=i##i;++i)
#define ROF(i,a,b) for(int i=a,i##i=b;i>=i##i;--i)
using namespace std;
const int N=1e3+7,M=3*N*N;
int n,m,C=1,lm,ct[M],T,ans[N],h[N],id[N],res=M,X[N],Y[N],g;
char s[N];
struct mem{int t,p,v;};
struct unf{
int a[M],C=0;mem f[M];
int& operator[](int x){return a[x];}
void st(int x,int v){f[++C]={T,x,a[x]},a[x]=v;}
void rbk(int t){for(;C&&f[C].t>=t;--C)a[f[C].p]=f[C].v;}
}fa,rk;
int rbk(int t){return fa.rbk(t),rk.rbk(t),T=t-1;}
int fd(int x){return fa[x]==x?x:fd(fa[x]);}
int merge(int x,int y){
x=fd(x),y=fd(y);
if(x!=y){
if(rk[x]<rk[y])swap(x,y);
ct[T+1]=ct[T]-1,++T,fa.st(y,x);
if(rk[x]==rk[y])rk.st(x,rk[x]+1);
}
return ct[T]==1;
}
struct edge{
int u,v,x,y;
void mtn(){
u=fd(u),v=fd(v);
if(u==v)x=m+1,y=n+1;
}
int add(int o){return x<=o?merge(u,v):0;}
}x[M],y[M];
void solve(int l,int r,int L,int R){
if(L==R)FOR(i,l,r)ans[i]=L;
if(l>r||L>R)return;
int d=(l+r)/2,G,t=T+1,p=R;
FOR(i,X[l],X[d+1]-1)if(x[i].y<L)x[i].add(M);
G=T+1;
FOR(i,Y[L],Y[R+1]-1)if(y[i].add(d)){p=y[i].y;break;}
ans[d]=p,rbk(G),solve(d+1,r,L,p),rbk(t);
FOR(i,Y[L],Y[p]-1)y[i].add(l-1);
solve(l,d-1,p,R);
}
void link(int i,int j){
if(id[i]&&id[j])++C,x[C]=y[C]={id[i],id[j],max(j-i-1,0),max(abs(h[j]-h[i])-1,0)};
}
signed main(){
scanf("%d%d",&n,&m),lm=max(n,m);
FOR(i,1,n){
scanf("%s",s+1),s[m+1]='1';
int w=1;
FOR(j,1,m)if(s[j]=='1'){
if(id[j])++C,x[C]=y[C]={ct[0]+1,id[j],0,i-h[j]-1};
g=h[j],id[j]=++ct[0],h[j]=i;
for(int p=j-1,k=g;p>=w;--p)if(h[p]>k)k=h[p],link(p,j);
for(int p=j+1,k=g;s[p]<'1';++p)if(h[p]>k)k=h[p],link(j,p);
w=j;
}
}
FOR(i,1,ct[0])fa[i]=i;
FOR(i,1,C)if(!x[i].y)x[i].add(0);
FOR(i,1,C)x[i].mtn(),y[i].mtn();
sort(x+1,x+C+1,[](edge a,edge b){return a.x<b.x;});
sort(y+1,y+C+1,[](edge a,edge b){return a.y<b.y;});
while(x[C-1].y>n)--C;
ROF(i,C,1)X[x[i].x]=Y[y[i].y]=i;
ROF(i,m,1)if(!X[i])X[i]=X[i+1];
ROF(i,n,1)if(!Y[i])Y[i]=Y[i+1];
Y[n+2]=C+1,X[m+2]=C+1,solve(0,m,0,n+1);
FOR(i,0,m)if(ans[i]<=n)res=min(res,i+ans[i]);
printf("%d",res);
return 0;
}