HDU 4507 求指定范围内与7不沾边的所有数的平方和 (数位DP)
题意:
求区间[l,r]内所有与7无关的数的平方和(取模)
定义与7无关的数:
1.数字的数位上不能有7
2.数字的数位和不能是7的倍数
3.数字本身不能是7的倍数
分析:
状态的保存:
1.数位上不能有7: 只需枚举数位的数字的时候跳过7就好 if (i == 7) continue;
2.数位和不能是7的倍数: 那么开一维保存数位和除以7的余数
3.数字本身不能是7的倍数:再开一维保存数字除以7的余数
综上,dp[i][j][k]3个维度保存的数字属性分别是:
i : 当前处理的数位
j : 数位和%7 等于j
k: 数字本身%7等于k
对于具有上述属性的数,用dp保存它们的3个值:(用结构体)
cnt: 具有该属性的所有数字的个数
s :具有该属性的所有数字的和
ss:具有该属性的所有数字的平方和
为什么要保存这3个值?为了下面的计算
状态的转移:
关于状态转移,先简单的写这样一个式子:dp[i][j][k] = ∑dp[i-1][(j+dig)%7][(k*10+dig)%7](这里的求和符号不指加法,是一个抽象的意义)
其中dig是枚举的正在处理的数位i上所有可能的数字(这个式子只能帮助理解状态是如何转移的但是却不表示具体的运算,dp是结构体当然不能直接运算)
上面的等式,我们称等式左边表示总状态,等式右边为其子状态,显然总状态是等于所有子状态的“总和”(我说的状态的总和并非指加法运算)
那么怎么通过子状态算出总状态呢?
具体的计算:
先说几句废话:
对于1234这个数,它的数位上的数是1,2,3,4,它的数位和是1+2+3+4,它自身的数值是 1*1000+2*100+3*10+4
如果我知道数字234是与7无关的数,在其前面加一个1,也是与7无关的数,我是怎样计算在其前面加一个1之后的数的平方和的呢
(1*1000)^2 + 2*(1*1000)*234 + 234^2 这里相当于(1000+234)^2
注意:在具体的状态转移中,我们是不知道234这个值,我们只知道有这么一个子状态
另外,这只是一个数的平方,我们要求的是所有满足的数的平方和,所以最后具体的算式如下:
设总状态为ans,它其中一个子状态为tmp,枚举正在处理的这一数位上的数字为 i ,数位 i 在整个数字中具体的数值是i*10^p
那么有: //这里自己手算写写就会懂了,注意这里都是+=,都是加上这位数后面的个数跟状态。
(1) ans.cnt += tmp.cnt
(2) ans.s += tmp.s + [ i*10^p ]*tmp.cnt //之所以*数量,是因为后面有许多个符合条件的数,这一位可以跟后面所有符合条件的数搭配
(3) ans.ss += tmp.ss + 2*(i*10^p)*tmp.s + [(i*10^p)^2]*tmp.cnt //这里的中间部分两倍那里,比较坑。。。
之所以变态是因为这个数太大了,不懂不懂就爆long long 所以哪里都要%Mod,少一个Mod就wa,另外这题比较新,返回的是结构体,判断记忆化的时候,也是用结构体里的变量实现的,通过构造函数初始化
总结题目是个好习惯: 完全没有想到原来是可以用结构体dfs , 而且考虑的也不全面 ,虽然之前的打法粗糙的想法是过了案例 , 但仔细想想又不行 , 还是太年轻了
/* * 如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关—— 1、整数中某一位是7; 2、整数的每一位加起来的和是7的整数倍; 3、这个整数是7的整数倍; 求一个区间中与7无关的数的平方和 */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const long long MOD=1000000007LL; struct Node { long long cnt;//与7无关的数的个数 long long sum;//与7无关的数的和 long long sqsum;//平方和 }dp[20][10][10];//分别是处理的数位、数字和%7,数%7 int bit[20]; long long p[20];//p[i]=10^i Node dfs(int pos,int pre1,int pre2,bool flag) { if(pos==-1) { Node tmp; tmp.cnt=(pre1!=0 && pre2!=0); tmp.sum=tmp.sqsum=0; return tmp; } if(!flag && dp[pos][pre1][pre2].cnt!=-1) return dp[pos][pre1][pre2]; int end=flag?bit[pos]:9; Node ans; Node tmp; ans.cnt=ans.sqsum=ans.sum=0; for(int i=0;i<=end;i++) { if(i==7)continue; tmp=dfs(pos-1,(pre1+i)%7,(pre2*10+i)%7,flag&&i==end); ans.cnt+=tmp.cnt; ans.cnt%=MOD; ans.sum+=(tmp.sum+ ((p[pos]*i)%MOD)*tmp.cnt%MOD )%MOD; ans.sum%=MOD; ans.sqsum+=(tmp.sqsum + ( (2*p[pos]*i)%MOD )*tmp.sum)%MOD; ans.sqsum%=MOD; ans.sqsum+=( (tmp.cnt*p[pos])%MOD*p[pos]%MOD*i*i%MOD ); ans.sqsum%=MOD; } if(!flag)dp[pos][pre1][pre2]=ans; return ans; } long long calc(long long n) { int pos=0; while(n) { bit[pos++]=n%10; n/=10; } return dfs(pos-1,0,0,1).sqsum; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; long long l,r; p[0]=1; for(int i=1;i<20;i++) p[i]=(p[i-1]*10)%MOD; for(int i=0;i<20;i++) for(int j=0;j<10;j++) for(int k=0;k<10;k++) dp[i][j][k].cnt=-1; scanf("%d",&T); while(T--) { scanf("%I64d%I64d",&l,&r); long long ans=calc(r); ans-=calc(l-1); ans=(ans%MOD+MOD)%MOD; printf("%I64d\n",ans); } return 0; }