整除k的最大连续子区间(前缀和取模)(2017美团笔试)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整除k的最大连续子区间(前缀和取模)(2017美团笔试)相关的知识,希望对你有一定的参考价值。

题意:一个数n,给出n个数,再给一个数k。求能整除k的连续区间和所在区间的最大长度。bc85场1001的升级版。

题解:刚拿到题的时候没看清是连续区间,就瞎想dp。发现连续区间后,想尺取法,发现这道题是离散的,没法尺取,也没法二分。

           正解应该是前缀和取模。若(sum[j]-sum[i])%k==0则区间[i,j]的和是能整除k的。注意,前缀和的第一项是0。

           预处理完成后,考虑如何求最大区间长。从前往后扫一次,记录每个值最早出现的位置。从后往前扫一次,以便记录每个值最后出现的位置。当值为0时,则记录出现的最后位置,从第一个数到该位置的区间和一定能整除k。

技术分享
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,cnt;
int a[500100],sum[500100],ans[500100];
int temp[500010];
int b[500100],c[500100],d[500100];
int main(){
   while(scanf("%d",&n)!=EOF){
      memset(sum,0,sizeof(sum));
      memset(ans,0,sizeof(ans));
      for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i]; //前缀和
      }
      scanf("%d",&k);
      cnt=0;
      memset(temp,0,sizeof(temp));
      for(int i=1;i<=n;i++){
        ans[i]=sum[i]%k;     //前缀和取模 
        if(temp[ans[i]]==0){
            temp[ans[i]]++;
            b[ans[i]]=i;
            d[cnt++]=ans[i]; //保存值的种类
        }
        //printf("%d ",ans[i]);
      }//printf("\n");
      int res;
      memset(temp,0,sizeof(temp));
      for(int i=n;i>=1;i--){
        if(temp[ans[i]]==0){
            if(ans[i]==0){
                res=i;   //最远距离初始化为最后一个0的位置
            }
            temp[ans[i]]++;
            c[ans[i]]=i;
        }
      }
      for(int i=0;i<cnt;i++){
        //printf("tt: %d %d %d\n",d[i],b[d[i]],c[d[i]]);
        if(d[i]!=0){
            res=max(res,c[d[i]]-b[d[i]]);
        }
      }
      printf("%d\n",res);
   }
   return 0;
}
View Code

 

以上是关于整除k的最大连续子区间(前缀和取模)(2017美团笔试)的主要内容,如果未能解决你的问题,请参考以下文章

美团笔试题连续最大子序列整除k

能被 K 整除的最大连续子串长度

17-求连续数组和最大的序列

HDU 5289 Assignment

16-求连续数组和最大

PAT甲题题解-1007. Maximum Subsequence Sum (25)-求最大子区间和