入阵曲(二维前缀和+计数)
题目:
是一个简单的小问题。他写写画画,画出了一个n×m的矩阵,每个格子里都有一个不超过k的正整数。
小F想问问你,这个矩阵里有多少个不同的子矩形中的数字之和是k的倍数?如果把一个子矩形用它的左上角和右下角描述为(x1,y1,x2,y2),其中x1≤x2,y1≤y2;
那么,我们认为两个子矩形是不同的,当且仅当他们以(x1,y1,x2,y2)表示时不同;也就是说,只要两个矩形以(x1,y1,x2,y2)表示时相同,就认为这两个矩形是同一个矩形,你应该在你的答案里只算一次。
输入
输入第一行,包含三个正整数n,m,k。输入接下来n行,每行包含m个正整数,第
输出
输入一行一个非负整数,表示你的答案。
样例输入
2 3 2
1 2 1
2 1 2
样例输出
6
题目描述
这个题就是问你一共有多少个子矩阵是k的倍数
题解
这个题如果看成一维的就是k倍区间
具体原理:
对于任意一段区间[l,r]的和就是sum[r]-sum[l-1].
(sum[r]-sum[l-1])%k 保证了[l,r]这段区间要么%k等于0 要么比k小.
等于0这表示了正好是k的倍数 然后通过前缀和相同的数据来判断出剩下的k的倍数:(sum[r]−sum[l−1])(sum[r]−sum[l−1]).
变形后就是:sum[r]%k==sum[l-1]%k
然后回到这个题,和那个题的思路是一样的,就是要求一个二维前缀和
代码
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn=5e6+100; int a[410][410]; ll sum[410][410]; ll b[maxn]; ll cnt[maxn]; int main(){ int n,m,z; cin>>n>>m>>z; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]); if(sum[i][j]>=z){ sum[i][j]-=z; } } } ll ans=0; for(int i=0;i<n;i++){ for(int j=i+1;j<=n;j++){ cnt[0]=1; for(int k=1;k<=m;k++){ b[k]=(sum[j][k]-sum[i][k])%z; if(b[k]<0){ b[k]+=z; } ans+=cnt[b[k]]; cnt[b[k]]++; } for(int k=1;k<=m;k++){ cnt[b[k]]=0; } } } cout<<ans<<endl; }