前缀和与差分

1. K倍区间#

来源:第八届蓝桥杯省赛C++B组,第八届蓝桥杯省赛JAVAB组
原题链接

题目描述

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

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

输入格式

第一行包含两个整数 NK

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

输出格式

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

数据范围

1N,K100000,
1Ai100000

输入样例

5 2
1
2
3
4
5

输出样例

6

算法:(前缀和 + 哈希表)O(n)#

算法内容#

  • 暴力做法:
    我们先预处理前缀和,用ij暴力枚举所有区间,每一次判断区间[j, i]是否满足(s[i]s[j1]) % k = 0,若满足答案加1,但是会循环n22次,时间复杂度为n2,会TLE

  • 我们如何优化?
    我们可以只用i枚举区间的右端点,对于所有以i为右端点的区间我们要找的是i前面有多少数满足:
    s[i]s[i1] % k = 0 s[i] % k = s[i1] % k
    s[i]s[i2] % k = 0 s[i] % k = s[i2] % k
    s[i]s[i3] % k = 0 s[i] % k = s[i3] % k
    ···
    s[i]s[0] % k = 0 s[i] % k = s[0] % k
    问题转化为s[i]前面有多少个数满足它除以k的余数是s[i] % k,所以我们可以用哈希表记录s数组中i前面的所有的数除以k的每一种余数的出现次数,每一次循环查表s[i] % k的次数,并累加,然后更新哈希表。

  • 注意:
    1.答案最多为n22个,可能爆int;前缀和数组最大的值可能为NK个,也可能爆int
    2.最开始循环时,i=1,一定记得将s[0]的信息插到哈希表中,因为s[0]=0,所以s[0] % k = 0,出现1
    3.每一次循环结束记得把s[i]的信息加到哈希表中


C++代码#

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;

int n, k;
LL s[N], cnt[N];

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

    LL res = 0;
    cnt[0] = 1; //一定要初始化是前面的s0的信息,因为s0 = 0,所以s0 % k = 0出现一次
    for (int i = 1; i <= n; i ++ )
    {
        res += cnt[s[i] % k];
        cnt[s[i] % k] ++ ;
    }

    printf("%lld\n", res);

    return 0;
}

posted @   LiHaoyu  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩