题目大意:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;求一定区间内与7无关的数的平方和。
题解:
这个题约束条件很常见,但是要求的不是数的个数,而是平方和就有点恶心了。
首先考虑求数的个数
定义状态dp[pos][sum1][sum2]表示枚举到pos位,前面数位的和为sum1,前面数位形成的数对7取余的结果sum2.
最后的时候再判断sum1%7!=0并且sum2!=0就行了。
然后考虑求满足条件的数的和。
当枚举到pos位时,我们用ans累加的不再是后面数位满足条件的数的个数,而是后面数位的个数*i*10^pos+后面数位形成的数,相当于给后面数位的每个数都分配了i*10^pos。
最后考虑数的平方和。
用pre表示当前数位,next表示后面数位
(pre*10^pos + next)^2= (pre*10^pos)^2+2*pre*10^pos*next +next^2
因此我们需要算出(pre*10^pos)^2和2*pre*10^pos*(后面数位的和)+后面数位的平方和(递归得到)
e.g:
假设满足条件的数有234,245,266,
那么 234^2 + 245^2 + 266^2
= (200 + 34)^2 + (200 + 45)^2 + (200 + 66)^2 = 3*200^2 + 2*200*(34+45+66) + (34^2 + 35^2 + 66^2),
因此在枚举到2的时候,表达式里只有3,(34 + 45 + 66),(34^2 + 35^2 + 66^2)不知道
代码实现:
#pragma GCC optimize(2) #include <iostream> #include <algorithm> #include <cmath> #include <cstring> #include <cstdio> #include <cstdlib> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #define PI atan(1.0) * 4 #define E 2.718281828 #define rp(i, s, t) for (register int i = (s); i <= (t); i++) #define RP(i, t, s) for (register int i = (t); i >= (s); i--) #define ll long long #define ull unsigned long long #define mst(a, b) memset(a, b, sizeof(a)) #define lson l, m, rt << 1 #define rson m + 1, r, rt << 1 | 1 #define pii pair<int, int> #define mp make_pair #define pb push_back #define debug printf("ac\n"); using namespace std; inline int read() { int a = 0, b = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') b = -1; c = getchar(); } while (c >= '0' && c <= '9') { a = (a << 3) + (a << 1) + c - '0'; c = getchar(); } return a * b; } const ll mod = 1e9+7; int a[20],num; ll fac[20];//预处理10的幂次 struct node{ ll cnt;//与7无关的数的个数 ll sum;//与7无关的数的和 ll sqr;//与7无关的数的平方和。 node(int c=0,int s=0,int ss=0):cnt(c),sum(s),sqr(ss){} // node():{} }dp[20][20][20]; node dfs(int pos,int pre1,int pre2,int lead,int limit){ if(pos==-1) return node(pre1!=0&&pre2!=0); if(!lead&&!limit&&dp[pos][pre1][pre2].cnt!=-1) return dp[pos][pre1][pre2]; int up=limit?a[pos]:9; node ans; rp(i,0,up) { if(i==7) continue; node tmp=dfs(pos-1,(pre1+i)%7,(pre2*10+i)%7,lead&&i==0,limit&&i==a[pos]); //简单数位dp求与7无关的数的个数 ans.cnt=(tmp.cnt+ans.cnt)%mod; //求与7无关的数的和 //处理到第pos个数位时,加上i*10^pos * 后面的个数 ans.sum=(ans.sum+tmp.sum+tmp.cnt*fac[pos]%mod*i%mod)%mod;//相当于给后面数位的每个数都加上一个i*10^pos //求与7无关的数的平方和 //(pre*10^pos + next)^2= (pre*10^pos)^2+2*pre*10^pos*next +next^2 ans.sqr=(ans.sqr+tmp.sqr+2*i%mod*fac[pos]%mod*tmp.sum%mod)%mod; ans.sqr=(ans.sqr+(tmp.cnt*fac[pos]%mod*fac[pos]%mod*i%mod*i%mod))%mod; } if(!limit&&!lead) return dp[pos][pre1][pre2]=ans; return ans; } ll solve(ll x){ num=0; while(x) a[num++]=x%10,x/=10; return dfs(num-1,0,0,1,1).sqr; } int main(){ mst(dp,-1); int T=read(); ll l,r; fac[0]=1; rp(i,1,19) fac[i]=fac[i-1]*10%mod; rp(i,0,19) rp(j,0,19) rp(k,0,19) dp[i][j][k].cnt=-1; while(T--){ scanf("%I64d%I64d",&l,&r); printf("%I64d\n",((solve(r)-solve(l-1))%mod+mod)%mod); } return 0; } /* 1 1000000000000000000 968811219 1 100000000000000000 476468678 */
hdu4507——数位dp(计数求和)
最新推荐文章于 2022-02-14 09:49:09 发布