【数学+思维】ZZULIOJ 1531: 小L的区间求和

题目链接

  • 题目描述
在给定的一个整数序列中,小L希望找到一个连续的区间,这个区间的和能够被k整除,请你帮小L算一下满足条件的最长的区间长度是多少。
输入
第一行输入两个整数n、k。(1 <= n <= 105,1<=k<100)
接下来一行输入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;
}


原文地址:https://www.cnblogs.com/bigbrox/p/11347682.html