裁判官オンライン:不明
ラベル:いい質問、数、および接頭辞
タイトル説明
彼らは、次の3つの条件を満たしていれば01は、完璧な長方形の長方形01と呼ばれています。
(1)その4つの側面は1であります
(四辺除く)1の数と0(2)内部との差が1以下であります
(3)サイズは少なくとも2 * 2
あなたはどのくらいの真円01長方形を見つけることが可能な01のマトリックスを、与えられました。
エントリー
二つの整数n及びmの最初の行
次のn行、各ライン番号m、0又は1です。
輸出
出力数01完璧な長方形。
サンプル
入力
4 4
1 1 1 1
1 0 1 1
1 1 0 1
1 1 1 1
5 5
1 0 1 1 1
1 0 1 0 1
1 1 0 1 1
1 0 0 1 1
1 1 1 1 1
出力
3
3
ヒント
データDataの30%の範囲で、nおよびmの\([1,20] \) 、
データの60%を、nおよびmのデータ範囲\([1100] \) ;
データの100%に、nおよびm個のデータ範囲\([1300] \) 。
問題の解決策
60pts
\(O(N ^ 4) \) だけでどのようにノックすることができます。長方形の直接列挙コーナー。ランダムデータの場合は、次のコードでは、速く走ります。しかし、多くの場合は1とハングアップ。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=302;
int a[N][N],n,m;
int sum[N][N];
int le[N][N],ri[N][N],up[N][N],down[N][N];
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
void pre(){
register int i,j;
for(i=1;i<=n;++i){
int lst=0;
for(j=1;j<=m;++j){
if(a[i][j])le[i][j]=++lst;
else lst=0;
}
lst=0;
for(j=m;j>=1;j--){
if(a[i][j])ri[i][j]=++lst;
else lst=0;
}
}
for(j=1;j<=m;++j){
int lst=0;
for(i=1;i<=n;++i){
if(a[i][j])up[i][j]=++lst;
else lst=0;
}
lst=0;
for(i=n;i>=1;i--){
if(a[i][j])down[i][j]=++lst;
else lst=0;
}
}
for(i=1;i<=n;++i)for(j=1;j<=m;++j){
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
}
}
inline int calc(int x,int y,int x2,int y2){
int res=sum[x2][y2]-sum[x-1][y2]-sum[x2][y-1]+sum[x-1][y-1];
return res;
}
namespace p60{
void solve(){
register int i,j,k,l;
ll ans=0;
for(i=1;i<=n;++i)for(j=1;j<=m;++j)if(a[i][j]){
for(k=i+1;k<=i+down[i][j]-1;k++)for(l=j+1;l<=j+ri[i][j]-1;l++)if(a[k][l]){
if((k-up[k][l]+1<=i)&&(l-le[k][l]+1<=j)){
int tmp=calc(i+1,j+1,k-1,l-1),o=(k-i-1)*(l-j-1);
if(abs(o-2*tmp)<=1)++ans;
}
}
}
printf("%lld\n",ans);
}
}
int main(){
// freopen("matrix.in","r",stdin);
// freopen("matrix.out","w",stdout);
n=read(),m=read();
for(register int i=1;i<=n;++i)for(register int j=1;j<=m;++j)a[i][j]=read();
pre();
p60::solve();
}
100pts
データ範囲を参照して、ソリューションの複雑さが正でなければならない\(O(N ^ 3) \) 。
問題数行列のこのタイプのために、一般的な日常の練習があります:
\(O(N ^ 2) \) でカラムの時間列挙垂直境界線、\(O(N)\)経時的に掃引、接頭辞などとメンテナンスカウンタ。
STEP0。前処理
最初の前処理のものはs[i][j]
、発現され(i,j)的二维前缀和
たが、0は、-1としてカウントされる通常異なるプレフィックスと1 +1としてカウント。だから、何が良い扱われますか?以下のものが挙げられます。
for(i=1;i<=n;i++)for(j=1;j<=m;j++){
scanf("%d",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(a[i][j]==1?1:-1);
}
これは得ることができ(x,y)
、左上(x2,y2)
四角形「エリア」の右下隅(矩形01内の説明の便宜上、以下が使用される、「領域」は、差の数を表します)。数が面積がゼロである数1に等しい場合、矩形番号5、面積として0より大きい数は、-5、0でした。
inline int Area(int x,int y,int x2,int y2){
return s[x2][y2]-s[x-1][y2]-s[x2][y-1]+s[x-1][y-1];
}
STEP1。統計
まず、以下列挙する。
for(i=1;i<n;i++)for(j=i+1;j<=n;j++){//上下边
で考慮すべき年を要した(\ O(N))\時間で得られた\(I、Jの\)上下01のための完全なマトリックスの数。
まず、比較的低いアプローチを考えました。
以下のコード、左側及び右側直接的暴力統計\([K、L] \ ) 修飾範囲の矩形の数。それに見えるものの\(O(N ^ 3) \) が、実際に\(O(N ^ 2) \) (なぜならバック\(K = L + 1 \は ) ビットをジャンプ)。
for(k=1;k<=m;k++)if(a[i][k]&&a[j][k]){
int l=k;
while(l+1<=m&&a[i][l+1]&&a[j][l+1])l++;//找到
if(k==l)continue;
//暴力统计
int o1,o2;
for(o1=k;o1<=l;o1++)for(o2=o1;o2<=l;o2++){
if(是01完美矩形(i,j,o1,o2))计数;
}
k=l+1;
}
このボトルネック\(O(N ^ 2) \) 暴力の統計。
解決策は、接頭辞を使用することで、前に前処理しました。次のコードを参照してください。
使用差動思考。それぞれの時間が1未満の差があると、それが答えで認識されているかどうかをプレフィックスとクエリの前に法律上の列を見つけます。そして、彼自身のプレフィックスと統計情報を追加しました。
for(p=k;p<=l;p++)if(Area(i,p,j,p)==j-i+1){
int pre=Area(i+1,k+1,j-1,p-1)+base,now=Area(i+1,k+1,j-1,p)+base;
ans+=cnt[pre-1]+cnt[pre]+cnt[pre+1];
cnt[now]++;
}
長方形の面積が負であり得るので、できるように、基本量追加する\(ベース\)を。
そして、算出終了\([K、L] \ ) 後矩形だけでなく、空(CNTは、[] \)\アレイ、次のコードがクリアされ、そして上記抗に関する。
for(p=k;p<=l;p++)if(Area(i,p,j,p)==j-i+1){
int now=Area(i+1,k+1,j-1,p)+base;
cnt[now]--;
}
完全に時間の複雑さがある(が。O(N ^ 3)\)\、完全なコードは次のとおりです。
//枚举上下边,前缀和统计
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=305,base=N*N;
int n,m,a[N][N],s[N][N],cnt[2*N*N];
ll ans=0;
inline int Area(int x,int y,int x2,int y2){
return s[x2][y2]-s[x-1][y2]-s[x2][y-1]+s[x-1][y-1];
}
int main(){
scanf("%d%d",&n,&m);
register int i,j,k,p;
for(i=1;i<=n;i++)for(j=1;j<=m;j++){
scanf("%d",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+(a[i][j]==1?1:-1);
}
for(i=1;i<n;i++)for(j=i+1;j<=n;j++){//上下边
for(k=1;k<=m;k++)if(a[i][k]&&a[j][k]){
int l=k;
while(l+1<=m&&a[i][l+1]&&a[j][l+1])l++;
if(k==l)continue;
for(p=k;p<=l;p++)if(Area(i,p,j,p)==j-i+1){
int pre=Area(i+1,k+1,j-1,p-1)+base,now=Area(i+1,k+1,j-1,p)+base;
ans+=cnt[pre-1]+cnt[pre]+cnt[pre+1];
cnt[now]++;
}
for(p=k;p<=l;p++)if(Area(i,p,j,p)==j-i+1){
int now=Area(i+1,k+1,j-1,p)+base;
cnt[now]--;
}
k=l+1;
}
}
printf("%lld\n",ans);
}