问题描述
给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
输入格式
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出格式
输出一个整数,代表K倍区间的数目。
样例输入
5 2
1
2
3
4
5
样例输出
6
数据规模和约定
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
注意:
main函数需要返回0;
只使用ANSI C/ANSI C++ 标准;
不要调用依赖于编译环境或操作系统的特殊函数。
所有依赖的函数必须明确地在源文件中 #include <xxx>
不能通过工程设置而省略常用头文件。
提交程序时,注意选择所期望的语言类型和编译器类型。
import java.util.Scanner;
public class Main {
static int a[] = new int [1000001];
static long addcount[] = new long [100001];
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
long sum;
while (true) {
addcount[0] = 0;
sum = 0;
for (int i = 1; i <= n; i++) {
a[i] = scanner.nextInt();
addcount[i] = addcount[i-1]+a[i]; //用addcount[]存放前i的数的和
}
for(int i = 1;i <= n ;i++) { //用i控制区间大小,i等于1时区间大小为1,i等于n时区间大小为n
for (int j = 0; j <= n-i; j++) {
if ((addcount[j+i]-addcount[j])%k==0) {
sum++;
}
}
}
System.out.println(sum);
}
}
}
这种写法在蓝桥杯练习系统中测试后超时,此时的算法复杂度为O(n²),所以此时要想办法降低解题的时间复杂度,下面为我参考别人思路后写出
的代码,还是有一个测试点超时,此种方法在蓝桥杯测试系统得分为85。
import java.util.Scanner;
public class Main {
static int num[] = new int [1000001];
static int sum[] = new int [1000001];
static int cun[] = new int [1000001];
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int k = scanner.nextInt();
int count = 0;
for(int i = 1;i <= n;i++) {
num[i] = scanner.nextInt();
sum[i] =(sum[i-1]+num[i])%k;
count += cun[sum[i]];
cun[sum[i]]++;
}
System.out.println((count+cun[0])); //注意加上[0,i]区间和是k的倍数的情况
}
}
解题思路:
求区间[l,r]的和是k的倍数的个数。求区间和,我们可以通过前缀和来求出。我们规定sum[i]表示第1个元素到第i个元素的和。
那么sum[r] - sum[l-1]就是区间[l,r]的和。区间[l,r]的和是k的倍数即(sum[r] - sum[l-1])%k == 0 即sum[r]%k == sum[l-1]%k。
数列 1 2 3 4 5 mod = 2
对前1个数的和取模, 为1 之前有0个前缀和取模后为1,个数+0
对前2个数的和取模, 为1 之前有1个前缀和取模后为1,个数+1
对前3个数的和取模, 为0 之前有0个前缀和取模后为0, 个数+0
对前4个数的和取模, 为0 之前有1个前缀和取模后为0,个数+1
对钱5个数的和取模, 为1 之前有2个前缀和取模后为1,个数+2
这个时候0+1+0+1+2=4,此时忽略了从0到i的情况,上面的for循环是从i=1开始的,算出的是sum[0,1,1,0,0,1]中1到i的情况
当然可以在下面用一个for循环来进行计数
for (int i = 0; i <= n; i++) {
count += cun[sum[i]];
cun[sum[i]]++;
}
System.out.println(count);
这样写算法复杂度仍然为O(n),但事实上还是会增加一些计算时间。