入阵曲(二维前缀和+计数)

题目:

是一个简单的小问题。他写写画画,画出了一个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[l1])(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; 
     
}

 

 

posted @ 2021-07-29 12:32  lipu123  阅读(79)  评论(0)    收藏  举报