鏈接:https://ac.nowcoder.com/acm/contest/1112/K
時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 32768K,其他語言65536K
Special Judge, 64bit IO Format: %lld
題目描述
Bobo 在 ICPCCamp 買了一塊 的土地,其中有些格子是障礙。
他想選擇兩個矩形區域,建造兩座房子。
很明顯,用於蓋房子的區域不能包含障礙。同時,兩個區域不能相交(但是可以相鄰)。
Bobo 想知道所有可能不同方案的數量除以 的餘數。
輸入描述:
輸入包含不超過 10 組數據。
每組數據的第一行包含兩個整數
接下來 n 行中的第 i 行包含一個長度爲 m 的字符串 。
的第 j 位是 0
則表示第 i 行第 j 列的格子是空地,是 1
則表示該格子是障礙。
輸出描述:
對於每組數據,輸出一個整數表示所求的值。
示例1
輸入
2 2
00
01
輸出
5
示例2
輸入
3 4
1000
0001
0100
輸出
160
思路:
首先利用單調棧求出
,,,。
我們枚舉所有矩形的左上角(如圖中藍色矩形),即每個。
那麼位於圖中黑色和黃色矩陣所處位置的矩形一定不會與左上角在的矩形相交,因爲該區域的矩形的右下角都是在覆蓋範圍之外的。
那麼對於左上角在的個矩形,其對答案的貢獻爲。
但是這樣會產生重複,如圖中的黃色和藍色矩形,他們之間的貢獻會被算2次,這時需要去重。
枚舉每個左下角在的矩形,即,其所產生的重複爲。
#include<bits/stdc++.h>
using namespace std;
const int MAX=1e3+10;
const int MOD=1e9+7;
typedef long long ll;
char s[MAX][MAX];
int A[MAX][MAX];
int B[MAX][MAX];
int C[MAX][MAX];
int p[MAX];
stack<pair<int,int> >q;
void solve(int n,int m)//計算A[i][j] B[i][j] C[i][j]
{
memset(p,0,sizeof p);
memset(A,0,sizeof A);
memset(B,0,sizeof B);
memset(C,0,sizeof C);
for(int i=1;i<=n;i++)
{
while(!q.empty())q.pop();
q.push({0,i});
int ans=0;
for(int j=1;j<=m;j++)
{
if(s[i][j]=='1')p[j]=i;
while(!q.empty()&&q.top().second<p[j])
{
pair<int,int>now=q.top();q.pop();
ans-=(now.first-q.top().first)*(i-now.second);
ans=(ans%MOD+MOD)%MOD;
}
(ans+=(j-q.top().first)*(i-p[j]))%=MOD;
q.push({j,p[j]});
A[i][j]=ans;
A[i][j]+=((A[i][j-1]+A[i-1][j]-A[i-1][j-1])%MOD+MOD)%MOD;
A[i][j]%=MOD;
}
}
memset(p,0,sizeof p);
for(int i=1;i<=n;i++)
{
while(!q.empty())q.pop();
q.push({m+1,i});
int ans=0;
for(int j=m;j>=1;j--)
{
if(s[i][j]=='1')p[j]=i;
while(!q.empty()&&q.top().second<p[j])
{
pair<int,int>now=q.top();q.pop();
ans-=(now.first-q.top().first)*(now.second-i);
ans=(ans%MOD+MOD)%MOD;
}
(ans+=(j-q.top().first)*(p[j]-i))%=MOD;
q.push({j,p[j]});
C[i][j]=ans;
}
}
for(int i=1;i<=m;i++)p[i]=n+1;
for(int i=n;i>=1;i--)
{
while(!q.empty())q.pop();
q.push({0,i});
int ans=0;
for(int j=1;j<=m;j++)
{
if(s[i][j]=='1')p[j]=i;
while(!q.empty()&&q.top().second>p[j])
{
pair<int,int>now=q.top();q.pop();
ans-=(now.first-q.top().first)*(now.second-i);
ans=(ans%MOD+MOD)%MOD;
}
(ans+=(j-q.top().first)*(p[j]-i))%=MOD;
q.push({j,p[j]});
B[i][j]=ans;
B[i][j]+=((B[i][j-1]+B[i+1][j]-B[i+1][j-1])%MOD+MOD)%MOD;
B[i][j]%=MOD;
}
}
}
int main()
{
int n,m;
while(cin>>n>>m)
{
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
solve(n,m);
for(int i=1;i<=m;i++)p[i]=n+1;
ll tot=0;
for(int i=n;i>=1;i--)//計算D[i][j]的同時,計算貢獻
{
while(!q.empty())q.pop();
q.push({m+1,i});
int ans=0;
for(int j=m;j>=1;j--)
{
if(s[i][j]=='1')p[j]=i;
while(!q.empty()&&q.top().second>p[j])
{
pair<int,int>now=q.top();q.pop();
ans-=(now.first-q.top().first)*(i-now.second);
ans=(ans%MOD+MOD)%MOD;
}
(ans+=(j-q.top().first)*(i-p[j]))%=MOD;
q.push({j,p[j]});
tot+=1ll*(A[n][j-1]+A[i-1][m]-A[i-1][j-1])%MOD*ans%MOD;
tot=(tot%MOD+MOD)%MOD;
}
}
for(int i=1;i<=n;i++)//去重
for(int j=1;j<=m;j++)
{
tot-=1ll*C[i][j]*B[i+1][j-1]%MOD;
tot=(tot%MOD+MOD)%MOD;
}
printf("%lld\n",tot);
}
return 0;
}