暴力的做法是(O(n^2)),枚举左右端点(l)和(r),通过前缀和计算出(sum[l sim r] = sum[r] - sum[l-1]),但不足以通过此题。
题意可转化为:找两个端点(l)和(r),使得(sum[l sim r]\%k = (sum[r] - sum[l-1])\%k == 0),即(sum[r] equiv sum[l-1] mod k)。
于是我们统计出所有(sum[i] mod k)的余数,记为(cnt[sum[i]\%k])。
最后枚举余数((0 sim k-1)),假设当前枚举的余数为(m),则满足(sum[l sim r]\%k == m)的(l)和(r)配对的个数为(C_{cnt[m]}^2)。
注意点
由于(sum[0]=0),因此初始时(cnt[0]=1)。
const int N=1e5+10;
LL sum[N],cnt[N];
int n,k;
int main()
{
cin>>n>>k;
cnt[0]=1;
for(int i=1;i<=n;i++)
{
cin>>sum[i];
sum[i]+=sum[i-1];
cnt[sum[i]%k]++;
}
LL ans=0;
for(int i=0;i<k;i++)
{
ans+=cnt[i]*(cnt[i]-1)/2;
}
cout<<ans<<endl;
//system("pause");
return 0;
}
也可以采用下面更简洁的写法,不再是求组合数了,而是规定了一个顺序,每次求当前为右端点(r)的情况下,(1 sim i-1)中与(sum[r])同余模(k)的左端点(l)的个数。
const int N=1e5+10;
int sum[N],cnt[N];
int n,k;
int main()
{
cin>>n>>k;
LL ans=0;
cnt[0]=1;
for(int i=1;i<=n;i++)
{
cin>>sum[i];
sum[i]=(sum[i]+sum[i-1])%k;
ans+=cnt[sum[i]];
cnt[sum[i]]++;
}
cout<<ans<<endl;
//system("pause");
return 0;
}