题目:
https://www.luogu.org/problemnew/show/3941
显然可以用二维前缀和来做,但是需要枚举行列起点终点,复杂度O(n^4);
由于求k的倍数,所以我们考虑在矩形%k情况下的特殊性;
(sum[i]-sum[j])%k=0;
sum[i]%k=sum[j]%k;
所以我们可以枚举列的起点和终点,统计个前缀和的余数个数,然后统计就行了;
注意:1、mod k=0的矩形,本身就可以对答案做贡献,需要单独讨论;
2、对于其他余数,必须在数量上大于等于2时才会对答案做贡献;
3、需要long long,取模时,需要防止负数出现;
思考:
这个题让我想起了NOIP2012选择客栈;
对于这类题,我们需要考虑题目的特殊性质,对答案统计”前缀和”;
这样,可以不用枚举起点,在后面某个可行答案直接加上前缀和即可;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll sum[1001][1001],num[1000001],cnt[1000001];
ll n,m,Mod,ans;
void solve()
{
scanf("%lld%lld%lld",&n,&m,&Mod);
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
scanf("%lld",&sum[i][j]);
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
sum[i][j]=(sum[i][j]+sum[i][j-1])%Mod;//统计每一行的前缀和;
for(ll i=1;i<=m;i++)//枚举列的起点;
{
for(ll j=i;j<=m;j++)//枚举列的终点;
{
for(ll k=1;k<=n;k++)//枚举行,即枚举夹在i与j之间的所有矩形;
{
num[k]=(num[k-1]+sum[k][j]-sum[k][i-1]+Mod)%Mod;当前前缀和的余数;
cnt[num[k]]=0;//清零;
}
for(ll k=1;k<=n;k++)
{
if(!num[k]) ans++;//如果余数是0,本身就算做答案;
ans+=cnt[num[k]],cnt[num[k]]++;//先统计,再+,以保证只有数量大于2时才会对答案做贡献;
}
}
}
cout<<ans;
return;
}
int main()
{
solve();
return 0;
}