题目描述:
给一个n*m的01矩阵,问有多少个子矩阵满足恰有k个1
1<=n,m<=2500,0<=k<=6
题目分析:
分治真是太强了%%%
这种矩形问题,计算方法通常是要定一条边,然后两边限制统计方案。
考虑将矩形(1,n,1,m)划分(比如说按水平线一分为二,设mid=(1+n)/2),预处理出第 i i i列到第 j j j列恰有 t t t个1往上最多能延伸到哪,以及往下最多能延伸到哪,那么跨过mid的矩形就可以枚举上半部分的1的个数 t t t,答案就加上 ( u p [ i ] [ j ] [ t ] − u p [ i ] [ j ] [ t − 1 ] ) ∗ ( d o w n [ i ] [ j ] [ k − t − 1 ] − d o w n [ i ] [ j ] [ k − t ] ) (up[i][j][t]-up[i][j][t-1])*(down[i][j][k-t-1]-down[i][j][k-t]) (up[i][j][t]−up[i][j][t−1])∗(down[i][j][k−t−1]−down[i][j][k−t])。
这样就可以计算跨过mid的矩形,然后分治两边,每次划分长的一边,一层的复杂度是
O
(
n
m
+
min
(
n
,
m
)
2
k
)
O(nm+\min(n,m)^2k)
O(nm+min(n,m)2k),总复杂度就是
O
(
n
m
k
∗
l
o
g
(
n
m
)
)
O(nmk*log(nm))
O(nmk∗log(nm))吗?其实我不太会分析
预处理的方法可以看代码,
:
:
k
::k
::k的意思是访问全局变量。
Code:
#include<bits/stdc++.h>
#define maxn 2505
using namespace std;
int n,m,k,a[maxn][maxn],v1[maxn][maxn][8],v2[maxn][maxn][8];
char s[maxn];
long long ans;
void Divide(int D,int U,int L,int R){
if(L==R&&D==U) {ans+=(a[D][L]==k);return;}
if(U-D>R-L){
int mid=(U+D)>>1;
for(int j=L;j<=R;j++){
int tp1=0,tp2=0;
for(int i=mid+1;i<=U&&tp1<=k;i++) if(a[i][j]) v1[j][j][++tp1]=i;
for(int i=mid;i>=D&&tp2<=k;i--) if(a[i][j]) v2[j][j][++tp2]=i;
while(tp1<=k) v1[j][j][++tp1]=U+1;
while(tp2<=k) v2[j][j][++tp2]=D-1;
}
for(int j=L;j<=R;j++) for(int k=j+1;k<=R;k++){
int i1=1,i2=1;
for(int i=1;i<=::k+1;i++) v1[j][k][i]=v1[j][k-1][i1]<v1[k][k][i2]?v1[j][k-1][i1++]:v1[k][k][i2++];
i1=i2=1;
for(int i=1;i<=::k+1;i++) v2[j][k][i]=v2[j][k-1][i1]>v2[k][k][i2]?v2[j][k-1][i1++]:v2[k][k][i2++];
}
for(int j=L;j<=R;j++) for(int k=j;k<=R;k++) for(int t=0;t<=::k;t++)
ans+=(v1[j][k][t+1]-(t?v1[j][k][t]:mid+1))*((t<::k?v2[j][k][::k-t]:mid)-v2[j][k][::k-t+1]);
Divide(D,mid,L,R),Divide(mid+1,U,L,R);
}
else{
int mid=(L+R)>>1;
for(int i=D;i<=U;i++){
int tp1=0,tp2=0;
for(int j=mid+1;j<=R&&tp1<=k;j++) if(a[i][j]) v1[i][i][++tp1]=j;
for(int j=mid;j>=L&&tp2<=k;j--) if(a[i][j]) v2[i][i][++tp2]=j;
while(tp1<=k) v1[i][i][++tp1]=R+1;
while(tp2<=k) v2[i][i][++tp2]=L-1;
}
for(int i=D;i<=U;i++) for(int k=i+1;k<=U;k++){
int j1=1,j2=1;
for(int j=1;j<=::k+1;j++) v1[i][k][j]=v1[i][k-1][j1]<v1[k][k][j2]?v1[i][k-1][j1++]:v1[k][k][j2++];
j1=j2=1;
for(int j=1;j<=::k+1;j++) v2[i][k][j]=v2[i][k-1][j1]>v2[k][k][j2]?v2[i][k-1][j1++]:v2[k][k][j2++];
}
for(int i=D;i<=U;i++) for(int k=i;k<=U;k++) for(int t=0;t<=::k;t++)
ans+=(v1[i][k][t+1]-(t?v1[i][k][t]:mid+1))*((t<::k?v2[i][k][::k-t]:mid)-v2[i][k][::k-t+1]);
Divide(D,U,L,mid),Divide(D,U,mid+1,R);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++) a[i][j]=s[j]-'0';
}
Divide(1,n,1,m);
printf("%lld\n",ans);
}