【算法】K倍区间

有帮助就点个赞呗👍

1.题目

K倍区间

给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。

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

输入格式
第一行包含两个整数 N 和 K。

以下 N 行每行包含一个整数 Ai。

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

数据范围
1≤N,K≤100000,
1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6

2.思路

借鉴y总思路
思路:暴力求解O(n3) →前缀和优化O(n2)→数学知识及空间换时间O(n)

难点:
1.y总怎么化简成求:当R固定时,在L在[0,R-1]之间,所有sum[R]与sum[L]余数相等的数的个数

因为,按前缀和知识,(sum[R]-sum[L-1])%k==0,先将L-1换成L,随之L就是在[0,R-1]之间,

然后把括号拆开再移项,即可化简成sum[R]%k==sum[L]%k.

2.为什么还要加cnt[0]=1;

下面代码有备注,直接回答字太多

3.如何做到时间换空间

理解sum[R]%k==sum[L]%k这一行,y总直接将相同余数放入cnt数组中
因此无序判断,直接索引,即相加即可
代码体现在 res+=cnt[sum[i]%k]; 这一行

3.代码


#include<iostream>
using namespace std;
const int N=1000009;
typedef long long LL  ;
LL sum[N];
LL cnt[N];
int n,k;

int main()
{
    cin>> n >> k;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&sum[i]);
        sum[i]=sum[i]+sum[i-1];
    }


    /*调试用,打印各求和区间的范围
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=i-1;j++)
            printf("[%d,%d] ",j+1,i);
        cout<<endl;
    }
    cout<<endl;
    */
    LL res=0;
    cnt[0]=1;
    /*
    再看一遍y总说的:
    当R固定时,在L在[0,R-1]之间,所有sum[R]与sum[L]余数相等的数的个
    数
    因为y总是先统计,再累加,然后代码是从i=1开始循环,所以实际上res这一行执行了n次,
    而cnt执行了虽然也是n次,但是第n次时,统计的数已经没有意义了,所以只执行了
    [1,n-1]的有效范围,0被忽视掉了!
    所以必须加上cnt[0]=1这一句才满足“在L在[0,R-1]之间”(R指右边界,姑且认为是n)

    如果觉得太麻烦,可以试试先累加,再统计,便于理解,俩行已经在下面注释掉
    */
    for(int i=1;i<=n;i++)
    {
        res+=cnt[sum[i]%k];//先统计 出现与当前值求余的结果相同的元素个数
        cnt[sum[i]%k]++;//再累加当前的值求余的结果,并储存进cnt中

        //下面解法是先累加在统计,不用考虑cnt[]的影响
        //即按y总的意思: 当R固定时,在L在[1,R]之间,所有sum[R]与sum[L-1]余数相等的数的个数

        // cnt[sum[i-1]%k]++;//先累加,sum[i-1]代表sum[L-1]的余数,cnt[sum[i-1]%k]代表其余数的个数
        // res+=cnt[sum[i]%k];//再统计,直接统计sum[i]就行了
                                //即判断所有sum[R]与sum[L]余数相等的数的个数

        /*调试用:打印各个步骤的值
        for(int i=0;i<k;i++)
            printf("%d ",cnt[i]);
        printf("res=%d\n",res);
        */
    }
    printf("%lld",res);

}

4.编译环境

acwing传送门


其他:个人博客,欢迎参观🤭


                    难得写个笔记,点个赞再走呗~
                             肥肠感谢!
                                😁
  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值