蓝桥杯—K倍区间问题(C语言解法)
题目描述
给定一个长度为N的数列,A1, A2, … AN,如果其中一段连续的子序列 Ai, Ai+1, … Aj(i <=
j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
输入描述
第一行包含两个整数 N 和 K(1<=N,K<=10^5)。
以下 N 行每行包含一个整数Ai(1<=Ai<=10^5)
输出描述
输出一个整数,代表 K 倍区间的数目。
输入输出样例
输入
5 2
1
2
3
4
5
输出
6
代码与思路
- 嵌套循环不可行,可能是超时
- 借鉴了很多方法,大致有了思路
- 要 求出给定数据的K倍区间,要先求sum[i,j],使sum[i,j]mod k=0,因为sum[i,j]=sum[j]-sum[i-1],使sum[i,j]%k=0,即(sum[j]-sum[i-1])%k=0即 使sum[j]%k=sum[i-1]%k,从上述分析可以得知,如果两组数mod k后的值相同,则全部数据去掉这两组数据后,剩余的数据之和为k的倍数,因为这两组数据为连续的,所以去掉这两组数据后的数据也是连续的。
- 要求累计和的k倍区间,也就是求对k取模后数值相同的数组数量,从若干个对k取模后值相同的数组中任选两组,所有余数的组合方式的数量即为结果。所以首先要统计各累计和对k取模后余数的值及数量,然后对其进行排列组合,选出两组即可。C(n,m)=n!/(n-m)!m!,通过整理得到C(n,2)=(n(n-1))/2(!!注意总结,求C(n,3)=(n(n-1)(n-2))/3 )
- 注意取模为0时,可以单取1个,也可以取两个,所以模为零时要加1,即设置模为0时初始值为1(相当于加了一个不存在的,这样就可以实现单取了)
int main(){
long int tnum,k,i,num,pnum=0;//tnum记录数据总数,num记录输入的数据,pnum K倍区间的个数
long long sum[100001]={0};//记录输入数据的累计和
long long _modk[100001];//累计和除以k的余数,即mod k
long long _modnum[100001]={0};//记录余数相同的个数
_modnum[0]=1;
scanf("%d %d",&tnum,&k);//tnum==total number
for(i=1;i<=tnum;i++){
scanf("%d",&num);//读入数据并存入相应数组中
sum[i]=sum[i-1]+num;
_modk[i-1]=sum[i]%k;
}
for(i=0;i<tnum;i++){
_modnum[_modk[i]]++;
}
for(i=0;i<k;i++){
if(_modnum[i]){
pnum+=((_modnum[i]*(_modnum[i]-1))/2);//从余数相同的若干组选出两组的组合数,即C(n,2)的计算
}
}
printf("%lld",pnum);
return 0;
}
稍微改了下代码,可能更好理解
#include<stdio.h>
int sum[100000];
int modk[100000];
int count[100000];
int main(){
int n,k,num;
int i,res=0;
count[0]=1;
scanf("%d %d",&n,&k);
for(i=1;i<=n;i++){
scanf("%d",&num);
sum[i]=sum[i-1]+num;
modk[i]=sum[i]%k;
count[modk[i]]++;
}
for(i=0;i<k;i++){
if(count[i])res+=(count[i]*(count[i]-1))/2;
}
printf("%d",res);
return 0;
}