问题描述
给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
你能求出数列中总共有多少个K倍区间吗?
输入格式
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出格式
输出一个整数,代表K倍区间的数目。
样例输入
5 2
1
2
3
4
5
1
2
3
4
5
样例输出
6
数据规模和约定
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
题解
题目清晰明了,简单的,暴力枚举~~~
1 void fun(int *a, int n, int k) 2 { 3 int c = 0; 4 for(int i=0;i<n;i++){ 5 for(int j=i;j<n;j++){ 6 int sum = 0; 7 for(int t=i;t<j;t++){ 8 sum += a[t]; 9 } 10 if(!(sum%k)){ 11 c++; 12 } 13 } 14 } 15 cout<<c<<endl; 16 }
或
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 6 using namespace std; 7 8 typedef long long ll; 9 10 const int MAX = 100001; 11 12 int main() 13 { 14 int n, k; 15 cin>>n>>k; 16 ll a[MAX], b[MAX], s = 0; 17 memset(a, 0, sizeof(a)); 18 memset(b, 0, sizeof(a)); 19 ll c = 0; 20 for(int i=1;i<=n;i++){ 21 cin>>a[i]; 22 if(a[i]%k == 0) 23 c++; 24 s += a[i]; 25 b[i] += s; 26 } 27 b[0] = 0; 28 for(ll i=0;i<n-2;i++){ 29 for(ll j=i+2;j<=n;j++){ 30 int e = abs(b[j] - b[i]); 31 if(e%k == 0) 32 c++; 33 } 34 } 35 cout<<c<<endl; 36 37 return 0; 38 }
然后,超时了~~~
题目部分数据很大,枚举的时间复杂度为o(n^3)或o(n^2),超时还是很正常的!
在网上学习了很多篇文章之后,总算是明白了优化算法。
现将我的理解分享给大家!
设有一数列a,需要求的是k=2的k倍区间:
a = [1, 2, 3, 4, 5]
我们可以知道
任何区间[i, j],都可以表示为:前 j 项的和减去前 i-1 项的和,即 sum = S[j] - S[i-1]
如果这个区间是k倍区间,那么下列等式成立:
1 (S[j] - S[i-1])%k == 0
// 移项得 2 S[j]%k == S[i-1]%k
也就是说,我们只需要求出前n项和,即:
S[1], S[2], ..., S[n]
然后我们判断它们对k取模的值,
[1, 1, 0, 0, 1]
此时,我们只要随意的选取模相同的区间,那么就是满足题意的区间,但是当我们的 i 取0时,会有-1出现,因为我们的公式是
S[j]%k == S[i-1]%k
所以我们要在最前面添加一个值,但是又不能影响整个数列,很明显,我们要添加一个 0, 如下图:
图不是很好看,但是大致就是这意思!!!
换句话说,我们只需要求出相同的数的组合数就ok了。
解
1 #include<iostream> 2 #include<cstring> 3 4 using namespace std; 5 6 typedef long long ll; 7 8 const int MAX = 100009; 9 int n, k; 10 ll a[MAX], s[MAX], c = 0; 11 12 int main() 13 { 14 15 cin>>n>>k; 16 memset(a, 0, sizeof(a)); 17 memset(s, 0, sizeof(a)); 18 int x = 0, y = 0; 19 s[0] = 1; 20 for(int i=0;i<n;i++){ 21 cin>>x; 22 y = (y + x)%k; 23 s[y%k]++; 24 } 25 for(int i=0;i<k;i++){ 26 c += (s[i]*(s[i]-1))/2; 27 } 28 cout<<c<<endl; 29 return 0; 30 }
此外,我还看到另一种解法,可惜有一小地方没弄明白,这里放上代码(https://www.lucien.ink/archives/63/)
1 #include <cstdio> 2 3 int tmp, sum, cnt[100007], n, k; 4 5 long long ans; 6 7 int main() 8 { 9 scanf("%d%d", &n, &k); 10 for (int i = 1; i <= n; i++){ 11 scanf("%d", &tmp); 12 sum = (sum + tmp) % k; 13 ans += cnt[sum]++; 14 } 15 16 prisntf("%lld ", ans + cnt[0]); 17 return 0; 18 }