蓝桥杯 k倍区间及其超时改进

问题描述
  给定一个长度为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),但事实上还是会增加一些计算时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值