这道题最好可以做到 n2.5 n 2.5 ,但由于数据弱可以让一些错误的 n2 n 2 算法AC
将行和列分开考虑。
枚举每个点时用前缀和记录要减去的数(见注释)
枚举行数(列数)时使用每一行(列)的和的gcd的约数。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ll long long
#define N 1010
#define max(x,y) ((x)>(y) ? (x) : (y))
#define min(x,y) ((x)<(y) ? (x) : (y))
using namespace std;
inline int getint() {
int p,t=0;
char ch=getchar();
for(;ch!='-' && !(ch>='0' && ch<='9');ch=getchar());
if(ch=='-') ch=getchar(),p=-1;
else p=1;
for(;ch>='0' && ch<='9';ch=getchar()) {
t=t*10+ch-48;
}
return t*p;
}
int n,m,a[N][N],r,p[N],cnt,g[N],c;
ll rs,cs;
inline ll gcd(ll a,ll b) {
ll r=a%b;
while(r) {a=b,b=r,r=a%b;}
return b;
}
int main() {
// freopen("shrew.in","r",stdin);
// freopen("shrew.out","w",stdout);
n=getint();m=getint();
ll tot=0;
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
tot+=a[i][j]=getint();
}
}
rs=0;
for(int i=1;i<=n;i++) {
ll sum=0;
for(int j=1;j<=m;j++) {
sum+=a[i][j];
}
rs=(rs==0 ? sum : gcd(rs,sum));//每一行的和的gcd
}
int RS=min(rs,m);
cnt=0;
for(int i=1;i<=RS;i++) {
if(rs%i==0) {
g[cnt++]=i;//枚举约数
}
}
for(int ir=cnt-1;ir>=0;ir--) {
r=g[ir];
bool f=true;
for(int i=1;i<=n;i++) {
memset(p,0,sizeof p);//前缀和
for(int j=1;j<=m;j++) {
p[j]=p[j-1];
if(j>r) {
if(a[i][j]<p[j]-p[j-r] || (j==m && a[i][j]!=p[j]-p[j-r])) {f=false;break;}
//p[j]-p[j-r]是当前点要减去的数
else p[j]=a[i][j]+p[j-r];
//p[j]+=a[i][j]-(p[j]-p[j-r]);
} else {
if(a[i][j]<p[j] || (j==m && a[i][j]!=p[j])) {f=false;break;}
else p[j]=a[i][j];
}
}
if(!f) break;
}
if(f) break;
}
// printf("%d\n",r);
cs=0;
for(int j=1;j<=m;j++) {
ll sum=0;
for(int i=1;i<=n;i++) {
sum+=a[i][j];
}
cs=(cs==0 ? sum : gcd(cs,sum));
}
int CS=min(cs,n);
cnt=0;
for(int i=1;i<=CS;i++) {
if(cs%i==0) {
g[cnt++]=i;
}
}
for(int ic=cnt-1;ic>=0;ic--) {
c=g[ic];
bool f=true;
for(int j=1;j<=m;j++) {
memset(p,0,sizeof p);
for(int i=1;i<=n;i++) {
p[i]=p[i-1];
if(i>c) {
if(a[i][j]<p[i]-p[i-c] || (i==n && a[i][j]!=p[i]-p[i-c])) {f=false;break;}
else p[i]=a[i][j]+p[i-c];
} else {
if(a[i][j]<p[i] || (i==n && a[i][j]!=p[i])) {f=false;break;}
else p[i]=a[i][j];
}
}
if(!f) break;
}
if(f) break;
}
printf("%lld\n",tot/r/c);
return 0;
}