前缀和-蓝桥杯省赛-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
题意:
输
入
区
间
长
度
N
,
整
数
K
,
输入区间长度N,整数K,
输入区间长度N,整数K,
输
入
N
个
数
,
问
长
度
为
N
的
区
间
中
,
有
多
少
个
子
区
间
的
区
间
和
是
K
的
倍
数
。
输入N个数,问长度为N的区间中,有多少个子区间的区间和是K的倍数。
输入N个数,问长度为N的区间中,有多少个子区间的区间和是K的倍数。
题解:
对
区
间
和
问
题
的
处
理
,
考
虑
前
缀
和
。
对区间和问题的处理,考虑前缀和。
对区间和问题的处理,考虑前缀和。
ll sum[N];//前缀和数组
ll ans=0;
for(int R=1;R<=N;R++)
for(int L=1;L<=R;L++)
if((sum[R]-sum[L-1])%K==0) ans++;
枚
举
区
间
的
左
右
端
点
L
和
R
,
看
区
间
和
模
K
是
否
为
0
,
时
间
复
杂
度
O
(
N
2
)
,
T
L
E
。
枚举区间的左右端点L和R,看区间和模K是否为0,时间复杂度O(N^2),TLE。
枚举区间的左右端点L和R,看区间和模K是否为0,时间复杂度O(N2),TLE。
考
虑
能
否
优
化
掉
一
个
循
环
:
考虑能否优化掉一个循环:
考虑能否优化掉一个循环:
事 实 上 , 第 二 层 循 环 是 计 算 : 前 R − 1 项 前 缀 和 当 中 与 前 R 项 和 同 余 的 个 数 事实上,第二层循环是计算:前R-1项前缀和\ 当中与\ 前R项和\ 同余的个数 事实上,第二层循环是计算:前R−1项前缀和 当中与 前R项和 同余的个数
所 以 我 们 开 一 个 c n t [ N ] 数 组 , 来 存 储 余 数 为 i 的 前 缀 和 的 个 数 所以我们开一个cnt[N]数组,来存储余数为i的前缀和的个数 所以我们开一个cnt[N]数组,来存储余数为i的前缀和的个数
每 枚 举 一 个 R , 答 案 就 加 上 c n t [ s u m [ R ] % K ] ( 表 示 在 前 R − 1 项 和 中 余 数 为 s u m [ R ] % K 的 个 数 ) , 再 更 新 c n t [ s u m [ R ] % K ] + + , 表 示 当 前 枚 举 的 第 R 项 前 缀 和 。 每枚举一个R,答案就加上cnt[sum[R]\%K](表示在前R-1项和中余数为sum[R]\%K的个数),\\再更新cnt[sum[R]\%K]++,表示当前枚举的第R项前缀和。 每枚举一个R,答案就加上cnt[sum[R]%K](表示在前R−1项和中余数为sum[R]%K的个数),再更新cnt[sum[R]%K]++,表示当前枚举的第R项前缀和。
时 间 复 杂 度 O ( N ) 。 时间复杂度O(N)。 时间复杂度O(N)。
for(int i=1;i<=N;i++)
{
ans+=cnt[sum[i]%K];
cnt[sum[i]%K]++;
}
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int N,K;
ll cnt[maxn];//cnt[i]表示余数为i的数的个数
ll sum[maxn];
ll ans;
int main()
{
cin>>N>>K;
for(int i=1;i<=N;i++)//边读入边计算前缀和数组,因为原数组在后来不需要使用
{
scanf("%lld",&sum[i]);
sum[i]+=sum[i-1];
}
cnt[0]=1;///初始状态下,区间和为0的sum[0]已有1个
for(int i=1;i<=N;i++)
{
ans+=cnt[sum[i]%K];
cnt[sum[i]%K]++;
}
printf("%lld\n",ans);
return 0;
}