Description
给定一个长度为 N 的数列,A1,A2,⋯AN,如果其中一段连续的子序列 Ai,Ai+1,⋯Aj(i≤j) 之和是 K 的倍数,我们就称这个区间[i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
Input
第一行包含两个整数 N 和KMATH:0:(1≤Ai≤1e5)。
Output
输出一个整数,代表 K 倍区间的数目。
Sample 1
Input | Output |
---|---|
5 2 1 2 3 4 5 | 6 |
因为本题的数据范围是1e5,第一想法是用前缀和预处理,再用两层for模拟左端点和右端点,如果前缀和数组(s[r]-s[l-1])%k==0则说明这段区间是k倍区间,但是由于数据范围是1e5,两层for会TLE,所以需要优化,因为(s[r]-s[l-1])%k==0,可以知道当两段区间的余数相同时,则这段区间为我们想要的,可以用动态规划的方式,dp数组表示当前前缀和%k=i的前缀和个数,将两层for循环变为一层for循环,时间复杂度降低。
还有一点要注意的是,开变量的时候要long long ,因为我们前缀和数组是累加的,累加到最后数字会非常大,超出int类型2e10。
AC代码
#include<iostream>
using namespace std;
const int MAX=2e5+5;
typedef long long ll;
ll a[MAX];
ll sum[MAX];
ll n,k,ans;
ll dp[MAX];//表示当前前缀和%k=i的前缀和个数
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
sum[i]+=sum[i-1]+a[i];//预处理前缀和
}
dp[0]=1;
for(int i=1;i<=n;i++)
{
ans+=dp[sum[i]%k];//但之后有找到前缀和%k与前一个相同的时候,答案+1
dp[sum[i]%k]++;
}
cout<<ans<<endl;
return 0;
}