题目链接
入阵曲
solution
- 很容易想到预处理二维前缀和%k的余数,然后\(O(n^4)\)判断
- 考虑如何优化到\(O(n^3)\)
- 我们可以枚举子矩形的起点的\(x\)坐标与水平长度,于是问题就可转化为一维
- 在一维平面上,一个子矩阵的和可以转化为一个大的前缀矩阵和减去一个小的,显然只要这2个矩阵和%k的余数相同,对应的子矩阵就符合条件
- 于是可以\(O(n)\)扫一遍记录%k余\(i\)的前缀矩阵和有多少个,即可\(O(n^3)\)完成
code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
typedef long long ll;
const int N=450;
const int K=1e6+10;
int n,m,k,a[N][N],s[N][N],w[K];
ll ans;
inline int add(int x,int y){return (x+y>=k)?x+y-k:x+y;}
inline int dec(int x,int y){return (x-y<0)?x-y+k:x-y;}
int main(){
// freopen("rally.in","r",stdin);
// freopen("rally.out","w",stdout);
n=read();m=read();k=read();
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
s[i][j]=read(),s[i][j]%=k;
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
s[i][j]=dec(add(s[i][j-1],add(s[i-1][j],s[i][j])),s[i-1][j-1]);
}
for(int i=0;i<=m;++i){
for(int j=i+1;j<=m;++j){
for(int ww=0;ww<=n;++ww){
int nw=dec(s[ww][j],s[ww][i]);
ans+=w[nw];
++w[nw];
}
for(int ww=0;ww<=n;++ww){
int nw=dec(s[ww][j],s[ww][i]);
--w[nw];
}
}
}
printf("%lld",ans);
return 0;
}