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

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

一、题目

题目描述

给定一个长度为 N N N 的数列, A 1 , A 2 , ⋯ A N A_1,A_2, \cdots A_N A1,A2,AN,如果其中一段连续的子序列 A i , A i + 1 , ⋯ A j ( i ≤ j ) A_i,A_{i+1}, \cdots A_j(i \le j) Ai,Ai+1,Aj(ij) 之和是 K K K 的倍数,我们就称这个区间 [ i , j ] [i,j] [i,j] K K K 倍区间。

你能求出数列中总共有多少个 K K K 倍区间吗?

输入格式

第一行包含两个整数 N N N K K K ( 1 ≤ N , K ≤ 1 0 5 ) (1 \le N,K \le 10^5) (1N,K105)

以下 N N N 行每行包含一个整数 A i A_i Ai ( 1 ≤ A i ≤ 1 0 5 ) (1 \le A_i \le 10^5) (1Ai105)

输出格式

输出一个整数,代表 K K K 倍区间的数目。

样例 #1

样例输入 #1
5 2
1  
2  
3  
4  
5
样例输出 #1
6

提示

时限 2 秒, 256M。蓝桥杯 2017 年第八届

二、分析题目

1、通过题目可知

我们需要计算很多个区间内的值的和,通过这些区间内的和与k取模运算,判断这些区间是否符合条件。显然,这是一道前缀和题目,前缀和可以很轻松的计算一个区间内的值的和。
关于前缀和的知识,传送门:https://blog.csdn.net/m0_73096566/article/details/129129517

2、数学知识

解题还需要另外一个数学知识:同余运算,具体公式如下:
A%k=B%k,那么|A-B|%k=0。

3、结合1和2来看:

(1)根据同余运算公式和本题的关系:题目中所需要的是区间和整除,也就是这个公式的后半部分(|A-B|%k=0)。那么我们就需要想方设法的找到,两个有着相同余数的A和B。
(2)根据前缀和的知识,我们如果想知道一个区间(i,j)的区间和,只需要用S(j)-S(i-1)即可,正好完美符合3(1)中的A和B的定义。
(3)也就是说:我们需要在前缀和S(1)到S(n)中,去统计有着相同余数的S(i)个数:same_mod_cnt个,然后在same_mod_cnt中,选择两个即可组成一对A和B,那么只需要计算 C same   ⁣ ⁣ _  ⁣ ⁣  mod   ⁣ ⁣ _  ⁣ ⁣  cnt 2 \text{C}_{\text{same }\!\!\_\!\!\text{ mod }\!\!\_\!\!\text{ cnt}}^{2} Csame _ mod _ cnt2即可。

4、综上,大致思路为:

(1)计算所有元素的前缀和,sum[i]表示。
(2)对每一个前缀和sum[i]取余运算,计算每一种余数所对应的前缀和个数,same_mod_cnt[j]表示余数为j的前缀和个数。
(3)for循环same_mod_cnt数组,累加统计 C same   ⁣ ⁣ _  ⁣ ⁣  mod   ⁣ ⁣ _  ⁣ ⁣  cnt[j] 2 \text{C}_{\text{same }\!\!\_\!\!\text{ mod }\!\!\_\!\!\text{ cnt[j]}}^{2} Csame _ mod _ cnt[j]2的值。
(4)有一个点值得注意一下,就是余数为0的时候,余数为0的时候,不是 C same   ⁣ ⁣ _  ⁣ ⁣  mod   ⁣ ⁣ _  ⁣ ⁣  cnt[j] 2 \text{C}_{\text{same }\!\!\_\!\!\text{ mod }\!\!\_\!\!\text{ cnt[j]}}^{2} Csame _ mod _ cnt[j]2,而应该是 C same   ⁣ ⁣ _  ⁣ ⁣  mod   ⁣ ⁣ _  ⁣ ⁣  cnt[j] 2 \text{C}_{\text{same }\!\!\_\!\!\text{ mod }\!\!\_\!\!\text{ cnt[j]}}^{2} Csame _ mod _ cnt[j]2+same_mod_cnt[j],因为区间也可以只有一个数,此时每一个余数为0的前缀和都满足条件,都可以被整除,所以在j=0的时候(余数为0),要做一个特殊处理。
事实上:
C n 2 + n \text{C}_{\text{n}}^{2}+\text{n} Cn2+n

= n ! 2 ! ( n − 2 ) ! + n \frac{n!}{2!\left( n-2 \right)!}+\text{n} 2!(n2)!n!+n

= n ( n − 1 ) 2 + n \frac{n(n-1)}{2}+\text{n} 2n(n1)+n

= n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)

= C n+1 2 \text{C}_{\text{n+1}}^{2} Cn+12
所以,在统计j=0的时候,只需要比平常多统计1即可。

5、举例

样例输入 #1
3 5
1  
5
9  
样例输出 #1
2

分析

n=3,k=5;
nums[]:1,5,9;
sum[]:1,6,15;
same_mod_cnt[]:1,2,0,0,0;//正常情况
//但是由于4(4)的说明,所以应为:
same_mod_cnt[]:2,2,0,0,0;

for循环same_mod_cnt[]数组:
累加 C n 2 \text{C}_{\text{n}}^{2} Cn2: C 2 2 + C 2 2 \text{C}_{\text{2}}^{2}+\text{C}_{\text{2}}^{2} C22+C22=2

6、int超时

注意int会超时,设置为long long

三、代码

#include<iostream>
#include <vector>
using namespace std;

int main()
{
    //输入数据
    long long n,k;cin>>n>>k;
    vector<long long>mod_array(k,0);
    mod_array[0]=1;
    //事实上不用存储每个前缀和的值,因为每个前缀和在求余后就不会再使用了
    long long sum=0;
    for(long long i=0;i<n;i++)
    {
        long long num;cin>>num;
        sum+=(num%k);
        mod_array[sum%k]++;
        sum%=k;
    }
    
    long long cnt=0;
    for(long long i=0;i<k;i++)
    {
        cnt+=(mod_array[i]*(mod_array[i]-1))/2;
    }
    cout<<cnt<<endl;
    return 0;
}
  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值