题意:一个数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; }
时间: 2024-10-12 16:45:42