使用暴力做法只能过两组测试样例,其他都超时;
以下是优化做法:
题目描述
给定一个长度为 N的数列,A1,A2,⋯AN,如果其中一段连续的子序列 Ai,Ai+1,⋯Aj(i≤j) 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K倍区间。
你能求出数列中总共有多少个 k倍区间吗?
输入格式
第一行包含两个整数 N和 K(1≤N,K≤105)。
以下 N行每行包含一个整数 Ai(1≤Ai≤105)。
输出格式
输出一个整数,代表 K倍区间的数目。
输入输出样例
输入 #1复制
5 2
1
2
3
4
5
输出 #1复制
6
题解:
关键思路:任意一个前缀和可以表示为ak+b(a,b是正整数,a表示倍数,b表示前缀和对k求模),如果有另一个前缀和具有同样的
代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e5+10;
long long mod[N];
long long s[N];
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){//边输入,边处理
scanf("%ld",&s[i]);//输入数据
s[i]+=s[i-1];//预处理前缀和
mod[s[i]%k]++;//将每个数据映射在mod数组上
}
long long res=0;//答案
mod[0]+=1;//这里要对模等于0的点进行特殊处理;分析:mod[i]中,下标i表示前缀和对k取模的值,mod[i]储存 的是前缀和取模等于i的个数。对于每一个mod[i]中的值(可以为0或1),任选两个构成一个区间,可 能会重复,所以要除以2.但是注意,这是在i不等于0的时候。i等于0表示这个点刚好是k倍区间,除了 可以任选两个点构成目标区间外,任选一个点也能构成目标区间。
至于为什么mod[0]+=1,是因为可以假设在最前端加上一个点,这个点的mod[]也等于0;这样可以与 后面的mod[i]同步,将i=0的情况并入18行代码的for循环语句块,同步处理;
for(int i=0;i<k;i++)
res+=mod[i]*(mod[i]-1)/2;
cout<<res<<endl;
return 0;
}
值得一提的是,依然要使用long long来储存前缀和(取极限,储存1e5个1e5,数据量达到1e9次方以上,会“爆int",),并且答案res,取极限,也会爆int.