一. 题目
二. 初步思路
因为是解决区间上的问题,很容易想到用前缀和来解决。前缀和是o ( n ) 的时间复杂度,但后续枚举两个端点要 o ( n^2 ),对于2e10的数据,超时。
for (int i = 1; i <= n; i ++)
for (int j = i; j <=n; j ++)
{
if ((a[j] - a[i]) % k == 0)
ans ++;
}
三. 正确解法
固定左端点,去枚举右端点来把时间复杂度降到o ( n )。对于两个不同的区间,因为左端点都是原点,两个右端点相减得到的中间一段即为某个区间。所有区间的左端点都是最左边的零点。
对这个得到的区间模k取余,当余数为零是这个区间就符合条件。对于余数不为零的区间,只需要找到另一个余数相同的区间,两区间相减得到的区间就是k倍。
这样就把问题转化成找相同余数余数。
用map记录同一余数出现次数,用set来遍历所有余数。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL n, k, a, s, ans;
map<LL, LL> mp;
set<LL> se;
int main()
{
mp.clear();
mp[0] = 0;
cin >> n >> k;
for (int i = 1; i <= n; i ++)
{
cin >> a;
s = (s + a) % k;
mp[s] ++;
se.insert(s);
}
for (auto i:se)
ans += (mp[i] * (mp[i] - 1)) / 2; // 任取余数相同的两个区间,Cm2种
cout << ans + mp[0];
return 0;
}