前缀和的应用(洛谷p8649)

 看到连续子序列,而且这道题的严格的控制了时间,我们应该想到前缀和(不懂的小伙伴可以参考其他优秀博客前缀和与差分_c语言差分和前缀和-CSDN博客),假设我们按照题意构建好了一个前缀和数组。

接下来我们采取这样的思路:利用数学知识,我们知道,假设数字a对某个数s取模的值等于数字b对s取模的值,那么a-b就可以被s整除(假设a>b),既然如此,我们不妨让前缀和数组的每一项都对k取模,让每一项保存各自取模后的结果,这样的话,但凡数组用存在两个元素(比如prefix[i]==prefix[j])相等,那么[i,j]就是数列的一个k倍区间啦!  (大家细细品味)

AC代码:

#include<stdio.h>
#define ll long long
ll a[100002],b[100002];
ll result;
  int main(){
  	ll n,k,cur;
  	scanf("%lld%lld",&n,&k);
  	ll i;
  	for(i=1;i<=n;i++){
  		scanf("%lld",&cur);
  		a[i]=a[i-1]+cur;//构建前缀和数组 
	  }
	for(i=0;i<=n;i++){
		result+=b[a[i]%k];
		b[a[i]%k]++;
	} 
	printf("%lld",result); 
  	return 0;
  }

我们一般把前缀和数组的第一项赋值为0.这里的数组b是记录前缀和数组中每个数字出现的次数,比如:用样例 得到的前缀和数组是{0  ,1,3,6,10,15}经过取模后变成{0,1,1,0,0,1}

比如数字0出现了3次,b[3]就等于3     。 

	for(i=0;i<=n;i++){
		result+=b[a[i]%k];
		b[a[i]%k]++;
	} 

这里可能会比较难理解,这段代码执行的功能就是把取模后数组的相同元素配对,但是不是很常规。按照一般的思路,数组里面有2个1,配对的方式只要一种,有三个0,配对的方式有这么3种。

有小伙伴可能会问,这个最初的0是我们自己赋值的,这样算会不会影响结果?其实,这正是把前缀和数组头元素置0的巧妙之处!因为如果前缀和数组的某个元素恰好能被k整除(对k取模为0)时,这就说明从开头元素到这个元素是k倍子区间。这种特殊的k倍子区间恰好可以由开头我们置的0和前缀和数组里面其他的0配对而成!可以帮助我们省去麻烦的分类讨论!!

按一般的思路来说,应该先把b[i]算出来,然后有b[i]!/2种配对方式,但计算阶乘可能会超时,上面的代码同样可以实现计算配对方式数目,大家可以自行模拟体会,在今后可以利用这种方式计算配对数目!

蟹蟹大家!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值