hdu-4507 吉哥系列故事——恨7不成妻 数位DP 状态转移分析/极限取模
http://acm.hdu.edu.cn/showproblem.php?pid=4507
求[L,R]中不满足任意条件的数的平方和mod 1e9+7。
条件:
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
首先想到数位DP,我们看下如何维护。
最基本的dp需要两维来维护起始数字和长度,此外对于数位求和mod 7的余数需要一维来维护,对于一个数mod 7的余数需要一维维护。
此外我们处理一下平方和,对于一个x开头,长度为len的xoo型数集,把它分成x*10^(len-1)+oo两部分,平方展开(x*10&(len-1))^2+2*(x*10^(len-1))*oo+oo^2,而我们要得到的是上式对于每一个oo的组合。
第一项的系数应该是oo的种类数(即最基本的数位DP计数)。
第二项的可以转化为2*(x*10^(len-1))*(oo1+oo2+...),即我们维护一下oo所有取值的和(维护方式类比维护平方)。
第三项即oo^2的和,我们递归地维护一下。
所以最后一维我们开[3]来维护基本数位计数,数值和,数值平方和。
对于出现7的处理,会做数位DP都会处理。
此题数据很接近long long max,请仔细取余(还得分%7 %1e9+7),不要偷懒~
#include <iostream> #include <cstring> #include <string> #include <queue> #include <vector> #include <map> #include <set> #include <stack> #include <cmath> #include <cstdio> #include <algorithm> #define LL long long using namespace std; const LL N = 1000015; const LL INF = 100000009; const LL mod = 1e9+7; const LL Len = 20; LL dp[Len][10][7][7][3];//bit,num,bitmod,valmod,sum(type: 0.cntSum 1.valSum 2.powSum LL bit[Len]; LL bitMod7[Len]; void init() { memset(dp, 0, sizeof dp);//初始化为0 for (LL i = 0; i < 10; i++) { if (i == 7)continue; dp[0][i][i % 7][i % 7][0] = 1; dp[0][i][i % 7][i % 7][1] = i; dp[0][i][i % 7][i % 7][2] = i*i; } bit[0] = 1; bitMod7[0] = 1; for (LL i = 1; i < Len; i++) { bit[i] = bit[i - 1] * 10; bit[i] %= mod; bitMod7[i] = bitMod7[i - 1] * 10; bitMod7[i] %= 7; } for (LL len = 1; len < Len; len++) { for (LL i = 0; i < 10; i++) { if (i == 7)continue; for (LL m = 0; m < 7; m++) { for (LL sm = 0; sm < 7; sm++) { LL aim = (m + i) % 7;//bitsum LL aism = (sm + i*bitMod7[len]) % 7;//valsum for (LL j = 0; j < 10; j++) { if (j == 7) continue; dp[len][i][aim][aism][0] += dp[len - 1][j][m][sm][0]; dp[len][i][aim][aism][0] %= mod; dp[len][i][aim][aism][1] += dp[len - 1][j][m][sm][1]; dp[len][i][aim][aism][1] %= mod; dp[len][i][aim][aism][1] +=(((dp[len - 1][j][m][sm][0] * i) % mod)*bit[len]) % mod; dp[len][i][aim][aism][1] %= mod; dp[len][i][aim][aism][2] += dp[len - 1][j][m][sm][2]; dp[len][i][aim][aism][2] %= mod; dp[len][i][aim][aism][2] += (((((dp[len - 1][j][m][sm][1] * 2) % mod)*i) % mod)*bit[len]) % mod; dp[len][i][aim][aism][2] %= mod; dp[len][i][aim][aism][2] += ((((((dp[len - 1][j][m][sm][0] * i) % mod*i) % mod)*bit[len]) % mod) * bit[len]) % mod; dp[len][i][aim][aism][2] %= mod; } } } } } } LL arr[100];//当前求解的数的分解 LL dfs(LL pos, LL preMod,LL presum,LL prebit) { if (pos == -1)return 0;//尾部 LL num = arr[pos];//获取当前数 LL cnt = 0;//计算以pre为前缀,后面从0~num-1开头的所有情况 for (LL i = 0; i < num; i++) { if (i == 7)continue; for (LL m = 0; m < 7; m++) { if ((m + prebit)%7 == 0) continue; for (LL sm = 0; sm < 7; sm++) { if ((presum + sm) % 7 == 0) continue; //cout<<preMod<<' '<<dp[pos][i][m] cnt += (((preMod*preMod) % mod)*dp[pos][i][m][sm][0])%mod; cnt %= mod; cnt += (((2 * preMod)%mod)*dp[pos][i][m][sm][1])%mod; cnt %= mod; cnt += dp[pos][i][m][sm][2]; cnt %= mod; } } } if (num == 7)return cnt%mod; return (cnt + dfs(pos - 1, (preMod+num*bit[pos])%mod,(presum+num*bitMod7[pos])%7,(prebit+num)%7)%mod)%mod;//下一级dfs传递前缀(对于不同题目需要传递的前缀信息不同) } LL sol(LL x) { if (x == 0) return 0; x++;//dfs在求解时,只能解出x-1的所有情况,x需要在递归尾部特判,干脆我们将x++,这样正好求出x LL siz = 0; while (x) arr[siz++] = x % 10, x /= 10; LL ans = 0; ans = dfs(siz - 1,0,0,0); return ans; } int main() { cin.sync_with_stdio(false); LL n, m; init(); int t; cin >> t; while (t--) { cin >> n >> m; cout << ((sol(m) - sol(n - 1))%mod+mod)%mod << endl; } return 0; }