- 题目描述
在给定的一个整数序列中,小L希望找到一个连续的区间,这个区间的和能够被k整除,请你帮小L算一下满足条件的最长的区间长度是多少。
输入
第一行输入两个整数n、k。(1 <= n <= 105,1<=k<100)
接下来一行输入n个整数,表示序列中的数。
接下来一行输入n个整数,表示序列中的数。
输出
输出一个整数,满足条件区间的最长长度,如果不存在,输出0
- 样例输入
5 7
1 2 4 1 1
- 样例输出
3
题解:
先将数组的前缀和数组构建起来。在构建过程中模K,所以此时前缀和数组中所有数属于[0,K-1],假如有两个位置i,j,前缀和数组pre[i] == pre[j],那么不模K的前缀和数组 pre[j]-pre[i]的结果一定是K的倍数,j-i就是长度。
所以我们的目的就成不断的了找:pre[i] == pre[j],保存长度 = j-i并更新。下面代码用:map首先都初始化为-1,之后遍历过程中遇到了就记录pre[i]的值第一次出现的位置
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int NN = 1e5; int N,K; int a[NN+10];//源数组 int pre[NN+10];//前缀和数组 map<int,int> mp; //mp[n] = i 表示前缀和数组中数字n在位置i第一次出现 int main(){ cin>>N>>K; for(int i =1;i<=N;i++){ //读入数据 scanf("%d",&a[i]); } for(int i = 1;i<=N;i++){ //构建前缀和数组并取余K pre[i] = (a[i]+pre[i-1])%K; mp[pre[i]] = -1;//都初始化为-1 } int maxlen = 0; //最长长度 for(int i = 1;i<=N;i++){ if(mp[pre[i]] == -1){ mp[pre[i]] = i;//改成第一次出现的位置 }else{ maxlen = max(maxlen,i-mp[pre[i]]);//i是pre[i]此时的位置 mp[pre[i]]是pre[i]第一次出现的位置,两个相减为k的倍数 } } cout<<maxlen<<endl; return 0; }
另一种用vector的写法,vector存前缀和数组元素出现的位置
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int NN = 1e5; int N,K; int a[NN+10];//源数组 int pre[NN+10];//前缀和数组 vector<int> ve[110]; int main(){ cin>>N>>K; for(int i =1;i<=N;i++){ //读入数据 scanf("%d",&a[i]); } ve[0].push_back(0); for(int i = 1;i<=N;i++){ //构建前缀和数组并取余K pre[i] = (a[i]+pre[i-1])%K; ve[pre[i]].push_back(i); } int maxlen = 0; for(int i = 0;i<K;i++){ if(ve[i].size()>1) maxlen = max(maxlen,ve[i][ve[i].size()-1] - ve[i][0]); //最后一次出现的位置 - 第一次出现的位置 } cout<<maxlen<<endl; return 0; }