目录:
【题目描述】
【AC代码】
【同余定理】
【作者思路】
【一些闲话】
【题目描述】
【AC代码】
在此先贴上博主的暴力代码,超时,只有28分
#include <bits/stdc++.h>
using namespace std;
int arr[100000];
int main()
{
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++)
{
cin>>arr[i];//输入
}
int f=0;
int cnt=0;
for(int i=0;i<n;i++)//暴力枚举每一个元素作为起始点向后相加的集合值
{
f=arr[i];//取出起始元素
if(f%k==0)//判断单个元素是否符合k倍
{
cnt++;
}
for(int j=i+1;j<n;j++)//以i为起点向后查找
{
f+=arr[j];//范围一直向后扩大
if(f%k==0)//符合k倍更新答案
{
cnt++;
}
}
}
cout<<cnt;
return 0;
}
接下来是100分代码
利用的前缀和以及同余定理
#include <bits/stdc++.h>
#define int long long
using namespace std;
int ans[100000];
signed main()
{
int n,k;
cin>>n>>k;
int f=0;
for(int i=0;i<n;i++)
{
int a;
cin>>a;
f+=a;
ans[f%k]++;//统计模k值相等的区间数量
}
int cnt=0;
for(int i=0;i<k;i++)
{
cnt+=((ans[i]-1)*ans[i])/2;//排列组合,在模k值相等的区间里找两个区间搭配
}
cout<<cnt+ans[0];//为什么加上ans[0],下文讲
return 0;
}
【同余定理】
当有两段区间除以 K 的余数相等时,它们的差就一定是 K 的倍数
【作者思路】
为什么要加上ans[0],ans[0]表示的意思是所有前缀和模k的值为0的前缀和个数,例如k=2,有前缀和f=4,6,8,所以ans[0]=3。此时最后一个for循环中计算的是两个前缀和的搭配,但是ans[0]中的区间不需要搭配,自己(一个前缀和)便符合题意,所以要加上只需要一个前缀和便符合题意的情况,因此输出应为cnt+ans[0]
【一些闲话】
欢迎各位指出错误,提出问题,作者一定尽力解答