一. 题目
二. 初步思路
因为是解决区间上的问题,很容易想到用前缀和来解决。前缀和是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;
}