题意:给定一个区间[a,b],求区间中和7无关的数的平方和。一个数如果某一位是7、每一位加起来是7的倍数或这个数是7的倍数,则这个数和7有关。
如果求的是个数,这道题明显可解,很裸的数位DP,但这道题求的是平方和。
注意到统计个数时,我们把个数当返回值往上传,因为递归到某一位时,这一位的数字已经都枚举过了,只要把个数累加即可。
求平方和时,不能直接累加,我们假设递归到第k位,枚举数字now,求到前k-1位的所有可行数为a[1,n],前k位的平方和只需加上∑(ai+now*Pow10[k-1])²,展开各项得到∑ai²+2*now*Pow10[k-1]∑ai+n*now²*Pow10[2k-2]。所以,只需要保存当前为中可行数的个数、总和、平方和即可,封成结构体当参数传递。
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x);i<=(y);i++) #define DOR(i,x,y) for(int i=(x);i>=(y);i--) #define P 1000000007 typedef long long LL; using namespace std; int num[23]; struct node { LL cnt,sum,sqsum; }dp[23][7][7]; LL Pow10[43]; void init() { //预处理10的幂 LL f=1; FOR(i,0,40) { Pow10[i]=f; (f*=10)%=P; } return; } node dfs(int k,int num_rem,int tot_rem,bool ismax) { if(k==0)return (node){num_rem&&tot_rem,0,0}; if(!ismax && ~dp[k][num_rem][tot_rem].cnt)return dp[k][num_rem][tot_rem]; int maxer=ismax?num[k]:9; LL _cnt=0,_sum=0,_sqsum=0; FOR(i,0,maxer) { if(i==7)continue; node nxt=dfs(k-1,(num_rem+i)%7,(tot_rem*10+i)%7,ismax&&i==maxer); (_cnt+=nxt.cnt)%=P; (_sum+=nxt.sum+Pow10[k-1]*i%P*nxt.cnt%P)%=P; (_sqsum+=nxt.sqsum+2*i*nxt.sum%P*Pow10[k-1]%P+nxt.cnt*i*i%P*Pow10[2*k-2]%P)%=P; } if(!ismax)dp[k][num_rem][tot_rem]=(node){_cnt,_sum,_sqsum}; return (node){_cnt,_sum,_sqsum}; } node solve(LL x) { int p=0; while(x) { num[++p]=x%10; x/=10; } return dfs(p,0,0,1); } int main() { init(); int T; scanf("%d",&T); memset(dp,-1,sizeof(dp)); while(T--) { LL L,R; scanf("%lld%lld",&L,&R); printf("%lld\n",((solve(R).sqsum-solve(L-1).sqsum)%P+P)%P); } //如果是求模后的结果,这里一定要取得非负数 return 0; }