历届试题 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

 方法:前缀和 取模 

 代码

#include<iostream>
#include<algorithm>
using namespace std;
long long input[100010],show[100010];
int main()
{
	int n,k,tp;
	cin>>n>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>tp;
		input[i]=(input[i-1]+tp)%k;
		show[input[i]]++;
	}
	long long ans=0;
	for(int i=0;i<k;i++)                //0<i<k 因为mod k之后最大是k-1 
		ans+=show[i]*(show[i]-1)/2;
	
	cout<<ans+show[0];

	return 0;
}


 注意点

 1 每次都取模然后求前缀和,简化计算

2 用一个show数组存放前缀和 

  1 2 3 4 5的前缀和为 1 1 0 0 1 那么show[0]=2,show[1]=3;

  show数组从0到k-1 因为mod k之后最大是k-1 

3 在存放完前缀和后就需要计算结果了

我们需要注意到 前缀和相同的两个位置i,j (比如示例中的1和5两个数字),

那么(i,j] 这个位置区间内的数字是k倍区间(2 3 4 5这些数字是k倍区间)

也就是说任意两个前缀和相等的位置可以组成一个k倍区间

那么就好办了 我们已经通过show数组记录了各种前缀和的数量

只要计算之和就可以了 

还需要加上前缀和等于0的那些数量 得到结果

 4 可能讲的大家听不太明白 我就按示例演示一遍吧

5 2

1 2 3 4 5 

可以求得前缀和数组 input[1]=1  input[2]=1  input[3]=0  input[4]=0  input[5]=1

然后我们得到前缀和结果数组show[0]=2 show[1]=3;

也就是说前缀和等于0的有2个 等于1的有3个

然后看等于1的这些位置 1 2  5

那么可以看出这三个位置任意选两个组合,他们中间的数之和就是前缀和

(1和2 中间(i,j]  也就是2 ; 1和5中间(i,j] 也就是2345 ; 2和5 中间也就是345  )

所以我们得到结论 从每种前缀和中任取两个都是k倍区间

那么怎么计算呢 其实就是排列组合  

然后我们注意到这样计算完之后ans=4,这是因为我们少了show[0]的区间;

可能会有人说之前我们不是用排列组合计算了show[0]嘛,其实不然,之前计算的是(i,j] 而单独看show[0] 是计数[i,j]

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherish_lii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值