输入样例:
5 2
1
2
3
4
5
输出样例:
6
这个题看到区间两个字,两眼一瞪可能就和前缀和差分有关
做题思路:
通过记录每个[1,r]区间值的和,看它前面是否出现过一个[1,l](l<=r),使得[l,r]区间的值能除尽k
有同学就好奇,这个双重循环,搞两个指针l,r,直接卡区间不就完了,为啥要左端点都从1开始,因为数据范围不允许O(N^2)的时间复杂度
如下面我的第一次错误代码
#include<iostream>
using namespace std;
const int N=100010;
int n,k,cnt;
long long a[N],f[N];
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
f[i]=f[i-1]+a[i];
}
for(int i=0;i<n;i++)
{
for(int j=i+1;j<=n;j++)//j一定严格大于i
{
if((f[j]-f[i])%k==0)
cnt++;
}
}
cout<<cnt;
return 0;
}
思路和逻辑没问题,就是太暴力了,导致时间复杂度太高,TLE超时了!/(ㄒoㄒ)/~~
下面是我看一个大佬的,他将两重循环,直接缩减到一层
画个简图也就是这个意思
#include<iostream>
using namespace std;
const int N=100010;
int n,k;
int a[N],f[N],ans[N];//这里面存的是取余后的结果所有定义为int就行
long long cnt;
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
f[i]=(f[i-1]+a[i])%k;
//其实相当于求除每个左端点为第一个元素的前缀和除k的余数
//然后如果前面存在余数相同的,说明咱们这个区间减去前面的区间结果为0,说明组合成的新区间可以除尽k
//则可以使用排列组合进行两两配对,也就是前面有几个余数相同的,则就可以生成几个k倍区间
cnt+=ans[f[i]];
ans[f[i]]++;
}
cout<<cnt+ans[0];//上面算的都是区间,最后要加上单个的,自己就可以除尽k的数(%k为0的数)
return 0;
}
太强了!膜拜大佬/(ㄒoㄒ)/~~