求某区间各个子集的和的根的最大的五个数。
子集根其实就是这个数模9后的余数,注意的是要区分0和9,两者的余数都是0。、
记录前i个数的和的根,设为sum数组,然后处理两个数组
lft[i][j]表示从i开始往右,最先sum出现根为j的位置。
rht[i][j]表示从i开始往左,最先sum开始出现根为j的位置。
处理一次询问,可以枚举状态,如枚举到j,当lft[i][j]<rht[i][j+k]成立时,则表明k这个根是存在的。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MAX=101000; int number[MAX],zero[MAX],lft[MAX][11],rht[MAX][11]; int cnt[MAX]; int sum[MAX],cts; int res[10]; int f(int m){ if(m==0) return 0; else if((m%9)==0) return 9; else return (m%9); } int main(){ int T,n,tmp,kase=0; scanf("%d",&T); while(T--){ printf("Case #%d: ",++kase); scanf("%d",&n); sum[0]=zero[0]=0; cnt[0]=0; for(int i=1;i<=n;i++){ scanf("%d",&tmp); sum[i]=f(sum[i-1]+tmp); cnt[i]=tmp==0?cnt[i-1]:cnt[i-1]+1; zero[i]=tmp==0?i:zero[i-1]; } cnt[n+1]=cnt[n]+1; for(int i=0;i<=9;i++)lft[n+1][i]=n+1,rht[0][i]=0; for(int i=n;i>=0;i--){ for(int k=0;k<=9;k++){ if(sum[i]==k) lft[i][k]=i; else lft[i][k]=lft[i+1][k]; } } for(int i=1;i<=n;i++){ for(int k=0;k<=9;k++){ if(sum[i]==k) rht[i][k]=i; else rht[i][k]=rht[i-1][k]; } } int qy,x,y,a,b; scanf("%d",&qy); while(qy--){ scanf("%d%d",&x,&y); memset(res,-1,sizeof(res)); for(int i=9,cts=5;i>0&&cts>0;i--){ int k; for(k=0;k<=9;k++){ a=k; b=a+i; if(b>9) b-=9; if(cnt[lft[x-1][a]]<cnt[rht[y][b]]) break; } if(k<=9) res[cts--]=i; } if(cts>0){ if(zero[y]>=x) res[cts--]=0; } printf("%d",res[5]); for(int i=4;i>0;i--) printf(" %d",res[i]); printf(" "); } if(T>0) printf(" "); } return 0; }