对于求区间的和,首先想到的就应该是用前缀和。
注:如果 S i S_i Si % k k k = = a , S j == a,S_j ==a,Sj % k = = a k == a k==a,则 ( S i − S j ) (S_i - S_j) (Si−Sj) % k = = 0 k == 0 k==0
假如现在对于一个右端点为 i i i的区间,如果长度为 1 1 1没那么就是 S i S_i Si - S i − 1 S_{i-1} Si−1,长度为二就是 S i − S i − 2 S_{i} - S_{i-2} Si−Si−2,…长度为 i i i就是 S i − S 0 S_{i} - S_{0} Si−S0,如果问这些数理有多少是 k k k的倍数,即求 S 0 , S 1 , S 2 , . . . . . . S i − 1 S_{0},S_{1},S_{2},......S_{i-1} S0,S1,S2,......Si−1中有多少个数与 S i S_{i} Si m o d mod mod k k k的余数相同,即求 S 0 S_0 S0到 S i − 1 S_{i-1} Si−1中与 S i S_i Si m o d mod mod k k k的余数相同的数出现了多少次,那么就可以直接用哈希表来存。
由于此题范围较小,所以可以直接用数组来模拟哈希表,用cnt[x]来求余数为
x
x
x的数的个数。
#include
using namespace std;
const int N = 1e5 + 10;
#define ll long long
int n, k;
int cnt[N];
ll s[N]; //前缀和数组
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> s[i];
s[i] += s[i - 1];
}
ll res = 0;
cnt[0]++; //s[0] % k = 0
for (int i = 1; i <= n; i++) {
res += cnt[s[i] % k];
cnt[s[i] % k]++; //这一步就使得i枚举到后边的时候,res加的时候不需要再重新算一遍前边的前缀和
}
cout << res;
return 0;
}