HDU 4507 吉哥系列故事——恨7不成妻 (数位DP)
题意:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关:
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
给定一个区间[L,R],问在此区间内和7无关的所有数字的平方和。
思路:
第一步好解决,只是数位DP的基础。第二步是十进制的所有位加起来是7的整数倍,这个只是需要用多一维来记录%7的结果就行了。第三步是7的整数倍问题,假设c=a+b,那么c%7=(a%7+b)%7,就假设这个数是10086,那么(10000%7+86)%7就行了,一样可以通过增加一维来解决。最后还要解决平方和问题,如第三步,(a+b)2=a2+2ab+b2,而∑(b2)部分已经在前面完成了,我们需要将其记录起来。剩下a2+2ab需要解决,∑(a2)直接算,∑(2ab)=2a*∑(b),所以∑(b)也是需要记录的,而求和∑就需要知道前面到底有几个数是合法的才行,则需要记录个数cnt。所以一共需要用到3维的数组,其中需要记录合法个数cnt,数的和sum,数的平方和ssum。
1 #include <bits/stdc++.h> 2 #define pii pair<int,int> 3 #define INF 0x7f3f3f3f 4 #define LL long long 5 #define ULL unsigned long long 6 using namespace std; 7 const double PI = acos(-1.0); 8 const int N=19; 9 const LL mod=1e9+7; 10 11 struct node 12 { 13 /* 14 1.与7无关的数的个数 15 2.与7无关的数的和 16 3.与7无关的数的平方和。 17 */ 18 ULL cnt; 19 ULL sum; 20 ULL ssum; 21 }dp[N][7][7]; 22 23 void pre_cal() 24 { 25 ULL base=dp[0][0][0].cnt=1; 26 for(int i=1; i<N; i++,base*=10 ) //位数 27 { 28 for(int u=0; u<10; u++) //第i位为u 29 { 30 if(u==7) continue; //不合法 31 ULL c=u*base%mod; 32 33 for(int j=0; j<7; j++) //位和 34 for(int k=0; k<7; k++) //数和 35 { 36 ULL a=(u+j)%7, b=(u*base+k)%7; 37 ULL sum =dp[i-1][j][k].sum,ssum=dp[i-1][j][k].ssum,cnt =dp[i-1][j][k].cnt;; 38 39 dp[i][a][b].cnt +=cnt; //个数 40 dp[i][a][b].cnt %=mod; 41 dp[i][a][b].sum +=sum +c*cnt%mod; //和 42 dp[i][a][b].sum %=mod; 43 dp[i][a][b].ssum+=ssum + 2*c*sum%mod + cnt*c%mod*c%mod;//平方和:3部分 44 dp[i][a][b].ssum%=mod; 45 } 46 } 47 } 48 } 49 50 int bit[N+2]; 51 ULL cal(ULL n) 52 { 53 memset(bit, 0, sizeof(bit)); 54 ULL base=1, ans=0, len=0, i, pre=0, bsum=0; //bsum记录前缀的位和 55 while(n) 56 { 57 bit[++len]=n%10; 58 n/=10; 59 base*=10; 60 } 61 for( i=len; i>0; i--) 62 { 63 base/=10; 64 for(int u=0; u<bit[i]; u++) //当前位 65 { 66 if(u==7) continue; 67 ULL c=(pre*10+u)*base%mod; //前缀,注意c已经取模了 68 for(int a=0; a<7; a++) //位和 69 for(int b=0; b<7; b++) //数和 70 if( (bsum+u+a)%7 && ( (pre*10%7+u)*base+b)%7 ) 71 { 72 ULL cnt=dp[i-1][a][b].cnt; 73 ULL sum=dp[i-1][a][b].sum; 74 ULL ssum=dp[i-1][a][b].ssum; 75 76 ans+= ssum + 2*c*sum%mod + cnt*c%mod*c%mod ; 77 ans%=mod; 78 } 79 } 80 pre=pre*10+bit[i]; 81 bsum+=bit[i]; //前缀的位和 82 if(bit[i]==7) break; //前缀出现了7,后面不可能了 83 } 84 return ans%mod; 85 } 86 87 int main() 88 { 89 //freopen("input.txt","r",stdin); 90 pre_cal(); 91 LL L, R; 92 int t;cin>>t; 93 while(t--) 94 { 95 scanf("%lld %lld",&L,&R); 96 printf("%llu\n", (cal(R+1)-cal(L)+mod)%mod ); 97 } 98 return 0; 99 }