全零子矩形計數問題

dcytrl發表於2024-11-10

經典問題,但是我為什麼不會呢?????

題意

給定一張 \(n\times m\) 的 01 矩陣,求出有多少個子矩陣使得子矩陣內沒有 1。

\(n,m\le 10^3\)

分析

考慮列舉每一行,計算以該行上每個點為右下角的合法子矩形個數 \(\sum sum_{i,j}\),也就是說,計算左上角的個數使得左上角和該右下角形成的子矩形不包含 1。

其實到這裡已經可以思考單調棧了,但是為了捋順思路,我們還是考慮對於每一行,只有該列上方的第一個 1 會對子矩形大小產生限制,而在某一個 1 往左的比該 1 所在行數要靠前的那些 1 也不會產生限制。據此我們考慮單調棧,預處理 \(up_{i,j}\) 表示 \((i,j)\) 往上第一個 1 和它本身的距離,單調棧裡維護一個遞增的 \(up_{i,j}\)\(\sum sum_{i,j}\),將比 \(up_{i,j}\) 大的點彈完後,令棧頂的列為 \(c\),由於單調棧 \(up\) 遞增,\(sum_{i,c}\) 的答案可以直接繼承到 \(sum_{i,j}\) 中,額外的貢獻還有 \(x\in(up_{i,c},up_{i,j}],y\in (c,j]\) 這部分子矩形的點,把它們加進 \(sum_{i,j}\) 中。

時間複雜度 \(O(nm)\)

rep(j,1,m){
	rep(i,1,n){
		if(s[i][j]=='1')up[i][j]=0;
		else up[i][j]=up[i-1][j]+1;
	}
}
int ans=0;
rep(i,1,n){
	tp=0;
	sum[0]=0;
	rep(j,1,m){
		while(tp&&up[i][j]<=up[i][sta[tp]])--tp;
		sum[j]=(sum[sta[tp]]+1ll*(j-sta[tp])*up[i][j]%mod)%mod;
		ans=(ans+sum[j])%mod;
		sta[++tp]=j;
	}
}

相關文章