P8649 [蓝桥杯 2017 省 B] k 倍区间:看的最懂的一集!

P8649 [蓝桥杯 2017 省 B] k 倍区间

题目:

P8649 [蓝桥杯 2017 省 B] k 倍区间

思路:

以后看到子序列问题,就应该想想前缀和,而为了判断是否是k的倍数,则会想到求模运算,在求模运算中,有一个经典的同余定理

前缀和

1. 什么是前缀和

所谓前缀和,就是:s[i] = s[i - 1] + a[i](i >=2)

例如样例 A = {1, 2, 3, 4, 5}
那么前缀和 S = {1, 3, 6, 10, 15}。

2. 前缀和性质

不仅Si是A的一个子序列,Si - Sj 也是A的一个子序列Aj+1 + Aj+2 +…+Ai这个子序的和,例如(下标从0开始):

S3 - S1 = 7
= A2 + A3 = 3 + 4

同余定理

如果两个数a和b他们对k取模,余数相同(即a mod k = b mod k),那么他们两个的差(a - b)对k取模为0。即:

(a - b) % k == 0
a - b 是k的倍数

解决了储备知识,下面正式开始解题!
显然根据前缀和的结构,如果前缀和 Si % k == 0 那么意味着这是一个满足题意的子区间,拿题目样例来看,S2 = 6 和 S3 = 10都满足,这里找到了两个,剩下的都不满足,那还能怎么利用前缀和呢?或者说,剩下满足题意的子序列和怎么找?
结合前缀和数组性质:S3 - S2 也是一个子序和,而且刚好等于4,并且4能被2整除!这似乎是一个惊人的发现!

即:余数都为0的两个前缀和数相减对2取余也为0

继续深入,前缀和数组中,S0、S1、S4他们模2的余数相同(都为1)!这时立马想到同余定理!即**S0、S1、S4两两组合**相减之后,对2取余不是刚好等于0吗!

所以:同余的前缀和数两两组合(本质是作差)便是一个可以满足题意的子序列和

而说到组合问题,想到公式:在这里插入图片描述

Cx2 = (x-1 + x)/2,那么除了余数为0的情况下,不做减法本身的前缀和数就已经满足条件,在计算时要额外加x个,其他余数的情况就是直接算组合数公式即可。

代码:

Java版:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;


public class Main {
	static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
	static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
	public static void main(String[] args) throws Exception {
		in.nextToken();
		long n = (long) in.nval;
		in.nextToken();
		long k = (long) in.nval;
		int count = 0;
		long t = 0;
		long[] modS = new long[(int) n];
		for(long i = 0; i < n; i++ ) {
			in.nextToken();
			t += (long) in.nval;
			modS[(int) (t % k)]++;
			if(t % k == 0) {	// 当前缀和数模k为0时,前缀数本身也需要加上
				count++;
			}
		}
		for(long i : modS) {
			count += (i - 1) * i / 2;
		}
		out.print(count);
		out.close();
	}
}

但是Java的不知道为什么,有一个测试数据就是过不了,可能是溢出了,这里就不花时间优化了,这本来是一道C语言组的题。
在这里插入图片描述

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值