DTOJ#5164. 鬼淵傳說

csdnzhanghongyu發表於2020-11-15

題面暫缺。

題解:
首先,對於不合法,有兩種情況,分別處理。
①內含白色連通塊。此時 O ( n 2 ) O(n^2) O(n2) 暴搜所有白色連通塊以及最小包含子矩形。可以發現,答案矩形不能包含子矩形。這時考慮優化 O ( n 4 ) O(n^4) O(n4) 暴力。
我們只用列舉上、下、左邊界,最大右邊界可以發現對於左邊界是單調增的。這時我們用桶維護。
②包含兩個及以上的黑色連通塊。這時我們可以發現對於兩列 r r r r + 1 r+1 r+1,假如我們把這兩列合併,可以發現若右邊界滿足①,那麼和並這兩列一定是合法的,不然必須有白色連通塊內含。所以我們可以算出 Δ s z \Delta sz Δsz 的字首和,就可以支援 O ( 1 ) O(1) O(1) 查詢左端點的答案。

#include<bits/stdc++.h>
#define N 305
using namespace std;
inline int read(){
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<1)+(x<<3)+s-'0';s=getchar();}
	return x*f;
}
int a[N][N],b[N][N],n,m,lx,ly,rx,ry,flag,st;
char s[N];
struct node{
	int sx,sy,tx,ty;
}sq[N*N];
inline bool cmp(node a,node b){return a.sx<b.sx;}
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
inline int pd(int x,int y){
	return x>=1&&x<=n&&y>=1&&y<=m;
}
void dfs(int x,int y){
	b[x][y]=1;
	lx=min(lx,x);ly=min(y,ly);
	rx=max(rx,x);ry=max(ry,y);
	for(int i=0;i<4;++i){
		int tx=x+dx[i],ty=y+dy[i];
		if(a[tx][ty])continue;
		if(!pd(tx,ty)){flag=1;continue;}
		if(b[tx][ty])continue;
		dfs(tx,ty);
	}
}
int ccl[N][N],fa[N*N*3],cnt1[N],cnt2[N],sum[N],tong[N*N<<1],zero=N*N,tl[N];
inline int id1(int x,int y){
	return (x-1)*m+y;
}
inline int id2(int x,int y,int typ){
	return (typ+1)*id1(n,m)+(x-1)*m+y;
}
inline int get(int x){return (fa[x]==x)?x:fa[x]=get(fa[x]);}
long long ans;
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i){
		scanf("%s",1+s);
		for(int j=1;j<=m;++j){
			a[i][j]=s[j]-'0';
		}
	}
    for(int i=1;i<=n;++i){
    	for(int j=1;j<=m;++j){
    		if(a[i][j]==0&&!b[i][j]){
    			lx=n+1,ly=m+1,rx=0,ry=0;flag=0;
    			dfs(i,j);
    			if(!flag){
    				sq[++st].sx=lx,sq[st].sy=ly,sq[st].tx=rx,sq[st].ty=ry;
    			}
    		}
    	}
    }
    sort(sq+1,sq+1+st,cmp);
    for(int ux=1,top=1;ux<=n;++ux){
    	for(int i=1;i<=m;++i)cnt1[i]=cnt2[i]=0;
    	for(int i=ux;i<=n;++i)for(int j=1;j<=m;++j)ccl[i][j]=1e9;
    	for(int i=ux;i<=n;++i)for(int j=1;j<=m;++j)fa[id1(i,j)]=id1(i,j),fa[id2(i,j,0)]=id2(i,j,0),fa[id2(i,j,1)]=id2(i,j,1);
    	for(int i=1;i<=m;++i)tl[i]=m;
    	while(sq[top].sx<=ux&&top<=st)++top;
    	for(int dx=ux,j=top;dx<=n;++dx){
    		while(j<=st&&sq[j].sx<=dx){
    			ccl[sq[j].tx+1][sq[j].sy-1]=min(ccl[sq[j].tx+1][sq[j].sy-1],sq[j].ty);
    			++j;
    		}
    		for(int i=1;i<=m;++i){
    			tl[i]=min(tl[i],ccl[dx][i]);
    		}
    		for(int i=m-1;i;--i)tl[i]=min(tl[i],tl[i+1]);
    		for(int i=1;i<=m;++i){
    			if(!a[dx][i])continue;
    			if(a[dx][i])cnt1[i]++;
    			if(a[dx-1][i]&&dx>ux){
    				fa[get(id1(dx,i))]=get(id1(dx-1,i));
    				cnt1[i]--;
    				//cout<<sq[j].sx<<" "<<sq[j].sy<<" "<<sq[j].tx<<" "<<sq[j].ty<<endl;
    			}
    		}
    		for(int i=2;i<=m;++i){
    			//if(ux==1&&dx==4)cout<<i<<" "<<get(20)<<endl;
    			//if(ux==1&&dx==4&&i==m)cout<<get(id2(dx,i))<<" "<<fa[id2(dx-1,i)]<<" "<<get(31)<<endl;
    			if(a[dx][i-1]){
				    cnt2[i]++;
				    if(a[dx-1][i-1]&&dx>ux){
				    	fa[get(id2(dx,i-1,i&1))]=get(id2(dx-1,i-1,i&1));
				    	cnt2[i]--;
				    	
				    }
				}
				if(a[dx][i]){
				    cnt2[i]++;
				    if(a[dx-1][i]&&dx>ux){
				    	fa[get(id2(dx,i,i&1))]=get(id2(dx-1,i,i&1));
				    	cnt2[i]--;
				    }
				}
				if(a[dx][i-1]&&a[dx][i]){
					int fl=get(id2(dx,i-1,i&1)),fr=get(id2(dx,i,i&1));
					if(fl!=fr){
						fa[fl]=fr;
						cnt2[i]--;
					}
				}
				sum[i]=cnt2[i]-cnt1[i-1];
			}
			for(int i=2;i<=m;++i)sum[i]+=sum[i-1];
			int l=m,r=m;
			for(;l;--l){
				tong[sum[l]+zero]++;
				while(r>tl[l]&&r)tong[sum[r]+zero]--,--r;
				ans+=tong[sum[l]-cnt1[l]+1+zero];
			}
			while(r)tong[sum[r]+zero]--,--r;
			//memset(tong,0,sizeof(tong));
			//for(int i=1;i<=m;++i)cout<<ux<<" "<<dx<<" "<<i<<":"<<f[ux][dx][i]<<endl;
			/*if(ux==1&&dx==4){
				for(int i=1;i<=m;++i)cout<<cnt2[i]<<" ";cout<<endl;
			}*/
		}
	}
	printf("%lld\n",ans);
	return 0;
}

相關文章