1. 程式人生 > >整除k的最大連續子區間(前綴和取模)(2017美團筆試)

整除k的最大連續子區間(前綴和取模)(2017美團筆試)

alt logs sed ace closed %d gif ring color

題意:一個數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美團筆試)