点击打开链接
点击打开链接
什么是数位 DP?
在信息学竞赛中,有一类难度不大但异常麻烦的问题——数位计数问题,这类问题的主要特点是询问的答案和一段连续的数的各个数位相关,并且需要对时间效率有一定要求。由
于解决这类问题往往意味着巨大的代码量,而众多的特殊情况又意味着出现错误的巨大可能性,因此很少有人愿意解决此类问题,但只要掌握好的方法,解决这类问题也并非想象中的那样困难。
——《数位计数问题解法研究》 高逸涵
在信息学竞赛中,有这样一类问题:求给定区间中,满足给定条件的某个D 进制数或此类数的数量。所求的限定条件往往与数位有关,例如数位之和、指定数码个数、数的大小顺序分组等等。题目给定的区间往往很大,无法采用朴素的方法求解。此时,我们就需要利用数位的性质,设计log(n)级别复杂度的算法。解决这类问题最基本的思想就是“逐位确定”的方法。下面就让我们通过几道例题来具体了解一下这类问题及其思考方法。
——《浅谈数位类统计问题》 刘聪
数位DP的解题思路
1.对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。
2.例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为:
3.[10000,19999],[20000,29999],[30000,39999],[40000,49999],[50000,54321]。
4.前四个区间如果可以直接解决,则只需处理最后一个区间,进一步将最后一个区间划分
5.为:[50000,50999],[51000,51999],[52000,52999],[53000,53999],[54000,54321]。同理将最后一
6.个区间划分下去,最后可以得到以下区间划分:
7.[10000,19999],[20000,29999],[30000,39999],[40000,49999],
8.[50000,50999],[51000,51999],[52000,52999],[53000,53999],
9.[54000,54099],[54100,54199],[54200,54299],
10.[54300,54309],[54310,54319],
11.[54320,54321]
数位DP的经典模板(记忆化搜索)
int dfs(int i, int s, bool e){
if(i==-1) return s = target_s;
if(!e && ~f[i][s]) return f[i][s];
int res = 0;
int u = e?num[i]:9;
for(int d = first?1:0; d<=u; ++d)
res += dfs(i-1, new_s(s, d), e&&d==u);
return e?res:f[i][s] = res;
}
f为记忆化数组;
i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);
s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);
e表示之前的数是否是上界的前缀(即后面的数能否任意填)。
for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,
表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.
于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。
//==============================================================================
经典题目
Hdu 2089 不要62
求给定区间中不含有62和4的数的个数。
///s=
///0 表示不含“62” ,“4”
///1 表示前一位为"6"
int dfs(int i, int s, int e) {
if(i==-1) return 1;
if(!e && ~dp[i][s]) return dp[i][s];
int res = 0;
int u = e?num[i]:9;
for(int d=0; d<=u; ++d) {
if(d==4) continue;
if(s&&(d==2)) continue;
res += dfs(i-1, d==6, e&&d==u);
}
return e?res:dp[i][s] = res;
}
Hdu 3555 Bomb
求给定区间求含有49的数的个数。
/*
s=
0 不含 “49”
1 前一位为“4”
2 含“49”
*/
inline int new_s(int s, int d) {
if(s==2 ||(s==1&&d==9)) {
return 2;
} else if(d==4) {
return 1;
} else
return 0;
}
LL dfs(int i, int s, int e) {
if(i==-1) return s==2;
if(!e && ~dp[i][s]) return dp[i][s];
LL res = 0;
int u = e?num[i]:9;
for(int d=0; d<=u; ++d) {
res += dfs(i-1, new_s(s,d), e&&d==u);
}
return e?res:dp[i][s]=res;
}
HYSBZ - 1026 windy数
求给定区间范围内的,求相邻数位之差绝对值不小于2的数的个数。
//s==0,表示有前缀零
int dfs(int i, int s, int e, int pre) {
if(i==-1) return s==1;
if(!e&&~dp[i][pre]&s) return dp[i][pre];
int res = 0;
int u = e?num[i]:9;
for(int d=0; d<=u; ++d)
if(!s||abs(pre-d)>=2)
res += dfs(i-1, s||d, e&&(d==u), d);
if (!e&&s) dp[i][pre] = res;
return res;
}
dfs(cnt-1, 0, 1, 11);
Hdu 3709 Balanced Number
平衡数。数n以数n中的某个位为支点,每个位上的数权值为(数字xi*(posi - 支点的posi)),如果数n里有一个支点使得所有数权值之和为0那么她就是平衡数。比如4139,以3为支点,左边 = 4 * (4 - 2) + 1 * (3 - 2) = 9,右边 = 9 * (1 - 2) = -9,左边加右边为0,所以4139是平衡数。现在给出一个区间[l,r],问区间内平衡数有多少个?
[cpp] view plaincopy
1.LL dfs(int pos,int o,int pre,int limit){
2. LL res=0;
3. if (pos<0) return pre==0;
4. if (!limit&&f[pos][o][pre]!=-1) return f[pos][o][pre];
5. int last=limit?dig[pos]:9;
6. for (int i=0;i<=last;i++){
7. res+=dfs(pos-1,o,pre+i*(pos-o),limit&&(i==last));
8. }
9. if (!limit) f[pos][o][pre]=res;
10. return res;
11.}
12.
13.LL solve(LL n){
14. if (n<0) return 0;
15. if (n==0) return 1;
16. int len=0;
17. while (n){
18. dig[len++]=n%10;
19. n/=10;
20. }
21. LL ans=0;
22. for (int i=0;i<len;i++){
23. ans+=dfs(len-1,i,0,1);
24. }
25. ans=ans-len+1;
26. return ans;
27.}
CodeForces 55D Beautiful numbers
如果一个数能够被其每个数位的数都整除,那么这个数就叫做美丽数。
[cpp] view plaincopy
1.int check(int bit,int mod){
2. for (int i=2;i<=9;i++){
3. if (bit&(1<<(i-2))){
4. if (mod%i) return 0;
5. }
6. }
7. return 1;
8.}
9.
10.LL dfs(int pos,int bit,int mod,int limit){
11. if (pos<0) return check(bit,mod);
12. if (!limit && f[pos][bit][mod]!=-1) return f[pos][bit][mod];
13. int last=limit?dig[pos]:9;
14. LL ret=0;
15. for (int i=0;i<=last;i++){
16. int nbit=bit;
17. if (i>=2) nbit|=1<<(i-2);
18. ret+=dfs(pos-1,nbit,(mod*10+i)%MOD,limit&&(i==last));
19. }
20. if (!limit) f[pos][bit][mod]=ret;
21. return ret;
22.}
Hdu 3652 B-number
求小于n是13的倍数且含有'13'的数的个数。
[cpp] view plaincopy
1.LL dfs(int pos,int pre,int fg,int md,int limit){
2. if (pos<0) return fg&&(md==0);
3. if (!limit&&f[pos][pre][fg][md]!=-1) return f[pos][pre][fg][md];
4. LL res=0;
5. int last=limit?dig[pos]:9;
6. for (int i=0;i<=last;i++){
7. res+=dfs(pos-1,i,fg||(pre==1&&i==3),(md*10+i)%13,limit&&(i==last));
8. }
9. if (!limit) f[pos][pre][fg][md]=res;
10. return res;
11.}
Hdu 4352 XHXJ's LIS
求[L,R]内最长递增子序列是k的数的个数;
[cpp] view plaincopy
1.LL f[maxn][1<<10][11];
2.int K;
3.int dig[maxn];
4.int getLIS(int bit){
5. int res=0;
6. for (int i=0;i<10;i++){
7. if (bit&(1<<i)) res++;
8. }
9. return res;
10.}
11.int gaoBit(int bit,int d){
12. if (bit&(1<<d)) return bit;
13. if ((1<<d)>bit) return bit|(1<<d);
14. bit|=(1<<d);
15. for (int i=d+1;i<10;i++){
16. if (bit&(1<<i)) return bit^(1<<i);
17. }
18. return 0;
19.}
20.
21.LL dfs(int pos,int bit,int limit){
22. if (pos<0) return getLIS(bit)==K;
23. if (!limit&&f[pos][bit][K]!=-1) return f[pos][bit][K];
24. LL res=0;
25. int last=limit?dig[pos]:9;
26. for (int i=0;i<=last;i++){
27.
28. int go;
29. if (bit==0&&i==0) go=0;
30. else go=gaoBit(bit,i);
31. res+=dfs(pos-1,go,limit&&(last==i));
32. }
33. if (!limit) f[pos][bit][K]=res;
34. return res;
35.}
36.
37.LL solve(LL n){
38. int len=0;
39. while (n){
40. dig[len++]=n%10;
41. n/=10;
42. }
43. return dfs(len-1,0,1);
44.}
HDU 4507 吉哥系列故事——恨7不成妻
中文题。需要推公式。
[cpp] view plaincopy
1.#include <iostream>
2.#include <cstring>
3.
4.using namespace std;
5.typedef long long LL;
6.const int maxn=22;
7.const int MOD=1e9+7;
8.
9.
10.typedef pair<LL,LL> PII;
11.typedef pair<PII,LL> PIII;
12.
13.inline LL fst(PIII a){
14. return a.first.first;
15.}
16.inline LL sec(PIII a){
17. return a.first.second;
18.}
19.inline LL thd(PIII a){
20. return a.second;
21.}
22.inline PIII makeZeroPIII(){
23. return make_pair(make_pair(0,0),0);
24.}
25.inline PIII makeOnePIII(){
26. return make_pair(make_pair(1,0),0);
27.}
28.LL power[maxn];
29.PIII f[maxn][11][8];
30.bool v[maxn][11][8];
31.int dig[maxn];
32.
33.PIII gao(PIII a, PIII b,int i,int pos){
34. PIII res=makeZeroPIII();
35. res.first.first=(fst(a)+fst(b))%MOD;//数量
36. res.first.second=(sec(a)+sec(b)+((i*power[pos])%MOD*fst(b))%MOD)%MOD;//和
37. res.second=(thd(a)+thd(b)+((2*i*power[pos])%MOD*sec(b))%MOD+(((i*i*power[pos])%MOD*power[pos])%MOD*fst(b))%MOD)%MOD;//平方和
38. return res;
39.}
40.
41.PIII dfs(int pos,int pre,int sev,int limit){
42. if (pos<0) {
43. if (pre!=0&&sev!=0) return makeOnePIII();
44. else return makeZeroPIII();
45. }
46. if (!limit&&v[pos][pre][sev]) return f[pos][pre][sev];
47. PIII res=makeZeroPIII();
48. int last=limit?dig[pos]:9;
49. for (int i=0;i<=last;i++){
50. if (i==7) continue;
51. PIII tmp=dfs(pos-1,(pre*10+i)%7,(sev+i)%7,limit&&(i==last));
52. res=gao(res,tmp,i,pos);
53. }
54. if (!limit){
55. v[pos][pre][sev]=true;
56. f[pos][pre][sev]=res;
57. }
58. return res;
59.}
60.
61.LL solve(LL n){
62. int len=0;
63. while (n){
64. dig[len++]=n%10;
65. n/=10;
66. }
67. PIII ans = dfs(len-1,0,0,1);
68. return thd(ans);
69.}
70.
71.int main()
72.{
73. memset(v,0,sizeof(v));
74. memset(power,0,sizeof(power));
75. power[0]=1;
76. for (int i=1;i<maxn;i++){
77. power[i]=(power[i-1]*10)%MOD;
78. }
79. int T;
80. cin>>T;
81. while (T--){
82. LL a,b;
83. cin>>a>>b;
84. cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl;
85. }
86. return 0;
87.}
POJ 3252 round number
统计一个区间内有多少个round number数?
"round number"定义:二进制形式中"0"的个数不小于"1"的个数。
//preZero=true表示当前位前面全为零
int dfs(int i, int sum0, int sum1, bool e, bool preZero) {
if(i==-1) return sum0>=sum1;
if(!e && ~dp[i][sum0][sum1]) return dp[i][sum0][sum1];
int res = 0;
int u = e?num[i]:1;
for(int d=0; d<=u; ++d) {
int t0 = sum0, t1 = sum1;
if(preZero || d) {
t0 +=(d==0);
t1 +=(d==1);
}
res += dfs(i-1, t0, t1, e&&(d==u), preZero||d);
}
return e?res:dp[i][sum0][sum1]=res;
}
Step2:撸中等题
1.hdu 4507
http://acm.hdu.edu.cn/showproblem.php?pid=4507
Tecent 2012.3.21 C 题
题目描述
求[l , r] 中
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
方法:
肯定是数位dp
必须这个满足减法——[l , r] = [0 , r] - [0 , l - 1]
我的算法是算相反的——即和数7有关的平方和,然后用1/6 * n * (n + 1) * (2n + 1) 来减一下
首先确定状态 —— dp[枚举位数][是否含有7][个数的和%7][这个数%7]
其次是维护:
1、个数
2、和 (由个数算出
3、平方和(由个数和 和 算出
首先说个数算法:
这就是一个最裸的数位dp,贴个模板就能做了 。。模板含义见资料
再说和的做法:(这里很容易错啊。。。)
在枚举第 i 位 是 d 的时候,算出 i - 1 位之后的个数 has ,那么这个d 就出现了 has 次,于是就要统计 d * 10的幂 * has 。
最后说平方和的算法:(卧槽这里写挂了好久好么。。。)
还是枚举第 i 位 是 d 的时候。我们看跟后面的关系。假设这个数是 dxxxxxxx(后面一大堆数字) 。假设这个数字是 d * 10的幂(设为 x) + y(后面一大堆数字),那么我们就是要计算(x + y)^2。 拆开来就是 x ^ 2 + 2xy + y ^ 2 。 首先 y ^ 2 在后面我们已经算过了,直接 搜索深度+1就可以计算,2xy 的话需要用 2 * x * (出现次数) * (后面数字的一次幂和), x ^ 2 的话直接 就 x ^ 2 乘以 后面的出现次数。
那么这个题目就解决了。。。
Debug方法:
卧槽数位dp debug 很关键好么。。
我这次的方法很科学,用个暴力的代码算出来比较坑爹的几个数字,比如 7 , 14 , 21 , 100 , 139 的三个部分 —— 个数 , 和, 平方和
然后,剥洋葱皮一样一点儿一点儿展开,先debug最外层的——个数,过掉几个数据之后说明可以信任;然后搞 和最后搞 平方和
Code
我的Code
[cpp] view plaincopyprint?
1.LL cnt[20][2][7][7] , dp[20][2][7][7] , dpsum[20][2][7][7];
2.LL ten[20];
3.LL num[20];
4.void init(){
5. ten[0] = 1;
6. REP_1(i , 19) ten[i] = (ten[i - 1] * 10) % MOD;
7.}
8.LL dfscnt(int i, bool seven , int numbersum, int sum , bool e) {
9. if (i==-1) return (seven || numbersum % 7 == 0 || sum % 7 == 0) ? 1 : 0;
10. if (!e && ~cnt[i][seven][numbersum][sum]) return cnt[i][seven][numbersum][sum];
11. LL res = 0;
12. int u = e?num[i]:9;
13. for (int d = 0; d <= u; ++d)
14. res = ( res + dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u)) % MOD;
15. return e?res:cnt[i][seven][numbersum][sum]=res;
16.}
17.LL mul(LL x , LL y){
18. x %= MOD;
19. y %= MOD;
20. return (x * y) % MOD;
21.}
22.LL dfssum(int i , int seven , int numbersum , int sum , bool e){
23. if (i == -1) return 0;
24. if (!e && ~dpsum[i][seven][numbersum][sum]) return dpsum[i][seven][numbersum][sum];
25. LL res = 0;
26. int u = e ? num[i] : 9;
27. for (int d = 0 ; d <= u ; ++d){
28. LL has = dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);
29. LL tmp = mul(d , ten[i]);
30. tmp = mul(has , tmp);
31. res = (res + dfssum(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u)) % MOD;
32. res = (res + tmp) % MOD;
33. }
34. return e ? res : dpsum[i][seven][numbersum][sum] = res ;
35.}
36.LL dfs(int i , int seven , int numbersum , int sum , bool e){
37. if (i == -1) return 0;
38. if (!e && ~dp[i][seven][numbersum][sum]) return dp[i][seven][numbersum][sum];
39. LL res = 0;
40. int u = e ? num[i] : 9;
41. for (int d = 0 ; d <= u ; ++d){
42. LL has = dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);
43. LL sum1 = dfssum(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);
44. LL tmp = mul(d , ten[i]);
45. LL nownow = mul(tmp , tmp);
46. LL hasnow = mul(nownow , has);
47. nownow = mul(2 , mul(tmp , sum1));
48. res = (res + dfs(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u)) % MOD;
49. res = (res + hasnow) % MOD;
50. res = (res + nownow) % MOD;
51. }
52. return e ? res : dp[i][seven][numbersum][sum] = res ;
53.}
54.LL l , r;
55.LL gao(LL x){
56. LL a = x , b = x + 1 , c = 2 * x + 1;
57. LL p = 2;
58. if (a % p == 0) a /= p;
59. else if (b % p == 0) b /= p;
60. else if (c % p == 0) c /= p;
61. p = 3;
62. if (a % p == 0) a /= p;
63. else if (b % p == 0) b /= p;
64. else if (c % p == 0) c /= p;
65. return mul(mul(a , b) , c);
66.}
67.LL getans(LL x){
68. if (x == 0) return 0;
69. int s = 0;
70. LL y = x;
71. while(x){
72. num[s ++ ] = x % 10;
73. x /= 10;
74. }
75. x = y;
76. LL res = gao(x);
77. res -= dfs(s - 1 , 0 , 0 , 0 , 1);
78. res %= MOD;
79. res += MOD;
80. res %= MOD;
81. return res;
82.}
83.void solve(){
84. RD(l , r);
85. LL ans = getans(r) - getans(l - 1);
86. ans %= MOD;
87. ans += MOD;
88. ans %= MOD;
89. printf("%I64d\n" , ans);
90.}
91.int main(){
92. FLC(dp , -1);
93. FLC(cnt , -1);
94. FLC(dpsum , -1);
95. init();
96. Rush solve();
97.}
AC的(不是很懂。。。)
[cpp] view plaincopyprint?
1.#include<cstdio>
2.#include<cstring>
3.#include<algorithm>
4.#include<cmath>
5.#include<vector>
6.#include<map>
7.#include<set>
8.#include<queue>
9.#include<string>
10.#include<iostream>
11.using namespace std;
12.
13.typedef long long LL;
14.typedef double db;
15.#define mp make_pair
16.#define pb push_back
17.
18.const LL P = (int)1e9 + 7;
19.LL f[20][9 * 20][7][2];//cnt
20.LL sum1[20][9 * 20][7][2];// sum = 1次和
21.LL sum2[20][9 * 20][7][2];//sum = 2次和
22.LL ten[100];
23.
24.void update(LL &a, LL b) {
25. a = (a + b) % P ;
26. if(a<0) a+=P;
27.}
28.
29.LL MOD(LL a) {
30. a %= P;
31. return a <0 ? a + P : a;
32.}
33.
34./*debug*/
35.LL _f[20][9 * 20][7][2];
36.LL _sum1[20][9 * 20][7][2];
37.LL _sum2[20][9 * 20][7][2];
38.
39.void _pre(){
40. _f[0][0][0][0]= 1;
41.
42. for(LL i = 1;i<=6;++i) {
43.
44. LL lo = 0, hi = ten[i]-1;
45. for(LL v = lo; v <= hi; ++ v){
46. LL rem = v % 7, dsum = 0;
47. LL k = v,msk = 0;
48. while(k){
49. LL d = k%10;
50. if(d == 7) msk = 1;
51. dsum += d;k/=10;
52. }
53. _f[i][dsum][rem][msk]++;
54. update(_sum1[i][dsum][rem][msk] , v);
55. update(_sum2[i][dsum][rem][msk] , (LL)v*v%P);
56. }
57. }
58. for(LL i = 0; i <= 6; ++ i) {
59. LL sumlim = i * 9;
60.
61. for(LL sum = 0; sum <= sumlim; ++ sum) {
62. for(LL modres = 0; modres < 7; ++ modres) {
63. for(LL has7 = 0; has7 < 2; ++ has7) {
64. if(_f[i][sum][modres][has7] != f[i][sum][modres][has7]) {
65. cout << "error at f!" << endl;
66. return;
67. }
68. if(_sum1[i][sum][modres][has7] != sum1[i][sum][modres][has7]) {
69. cout << "error at s1!" << endl;
70. cout << i <<" " << sum <<" " << modres << " " << has7 << endl;
71. cout <<_sum1[i][sum][modres][has7] <<" "<<sum1[i][sum][modres][has7]<<endl;
72. return;
73. }
74. if(_sum2[i][sum][modres][has7] != sum2[i][sum][modres][has7]) {
75. cout << "error at s2!" << endl;
76. cout <<_sum2[i][sum][modres][has7] <<" "<<sum2[i][sum][modres][has7]<<endl;
77. return;
78. }
79. }
80. }
81. }
82. }
83. cout <<"ok"<<endl;
84.}
85.
86.LL ten7[100];
87.
88.void pre(){
89. f[0][0][0][0] = 1;
90. ten[0] = 1; ten7[0]=1;
91. for(LL i = 1; i <= 99; ++ i) ten[i]=ten[i-1] * 10%P;
92. for(LL i = 1; i <= 99; ++ i) ten7[i]=ten7[i-1] * 10%7;
93.
94. for(LL i = 1; i <= 19; ++ i) {
95. for(LL j = 0; j < 10; ++ j) {
96. LL sumlim = (i-1) * 9;
97.
98. for(LL sum = 0; sum <= sumlim; ++ sum) {
99. for(LL modres = 0; modres < 7; ++ modres) {
100. for(LL has7 = 0; has7 < 2; ++ has7) {
101. LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas
102. = has7 | (j == 7);
103. if( nsum <= sumlim + 9) {
104. update(f[nbit][nsum][nmod][nhas],
105. f[i-1][sum][modres][has7]);
106. }
107. }
108. }
109. }
110. }
111. }
112.
113. // sum1
114. for(LL i = 1; i <= 19; ++ i) {
115. for(LL j = 0; j < 10; ++ j) {
116. LL sumlim = (i-1) * 9;
117.
118. for(LL sum = 0; sum <= sumlim; ++ sum) {
119. for(LL modres = 0; modres < 7; ++ modres) {
120. for(LL has7 = 0; has7 < 2; ++ has7) {
121. LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas
122. = has7 | (j == 7);
123. if( nsum <= sumlim + 9) {
124. update(sum1[nbit][nsum][nmod][nhas],
125. MOD( sum1[i-1][sum][modres][has7]+((ten[i-1]*j)%P)*f[i-1][sum][modres][has7]%P ) );
126. }
127. }
128. }
129. }
130. }
131. }
132.
133. // sum2
134. for(LL i = 1; i <= 19; ++ i) {
135. for(LL j = 0; j < 10; ++ j) {
136. LL sumlim = (i-1) * 9;
137.
138. for(LL sum = 0; sum <= sumlim; ++ sum) {
139. for(LL modres = 0; modres < 7; ++ modres) {
140. for(LL has7 = 0; has7 < 2; ++ has7) {
141. LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas
142. = has7 | (j == 7);
143. if( nsum <= sumlim + 9) {
144. LL tmp = 0, cnt = f[i-1][sum][modres][has7];
145. tmp = MOD(tmp + sum2[i-1][sum][modres][has7]);
146. tmp = MOD(tmp + (ten[2*i-2]*j*j)%P*cnt%P);
147. tmp = MOD(tmp + (ten[i-1]*j*2)%P*sum1[i-1][sum][modres][has7]%P);
148. update(sum2[nbit][nsum][nmod][nhas],
149. tmp );
150. }
151. }
152. }
153. }
154. }
155. }
156.}
157.
158.bool ok(LL x) {
159. LL dsum = 0;
160. if(x % 7 == 0) return true;
161. while(x) {
162. LL d = x % 10;
163. if(d == 7) return true;
164. dsum += d; x/=10;
165. }
166. return dsum %7 == 0;
167.}
168.
169.LL mul(LL a, LL b, LL c) {
170. a %= c;
171. b %= c;
172. return a*b % c;
173.}
174.
175.LL gao(LL x) {
176. if(x <= 0) return 0;
177. LL ans = ok(x)?0:mul(x,x,P);
178. //cout << x <<" ans = " << ans << endl;
179. LL has7 = 0;
180. LL sum = 0;
181. LL modres = 0;
182. LL pre = 0;
183.
184. LL d[22],dlen=0;
185. while(x) d[dlen++]=x%10,x/=10;
186.
187. for(LL i = dlen-1,j=0;i>=0;--i,++j) {
188. for(LL dg = 0; dg < d[i]; ++ dg) {
189. for(LL dsum = 0; dsum <= i*9; ++ dsum) {
190. for(LL md = 0; md < 7; ++ md) {
191. for(LL msk = 0; msk < 2; ++ msk) {
192.
193. LL mdsum = dsum + dg + sum;
194.
195. LL tmp = (modres*10 + dg)%7;
196.
197. LL mmd = (ten7[i]*tmp+md)%7;
198.
199. LL mmsk = has7 | msk | (dg == 7);
200.
201. if( mmsk == 1 || mdsum % 7 ==0 || mmd == 0) continue;
202.
203. LL dd = (pre * 10 + dg) % P;
204. LL cct = f[i][dsum][md][msk];
205.
206. update(ans, sum2[i][dsum][md][msk]);
207. update(ans, MOD(((dd*2%P)*ten[i])%P*sum1[i][dsum][md][msk]%P));
208. update(ans, ((((dd*dd%P)*ten[i])%P*ten[i])%P*cct)%P);
209. }
210. }
211. }
212. }
213.
214. if(d[i] == 7) has7 = 1;
215. sum += d[i];
216. modres = (modres*10+d[i])%7;
217. pre = pre * 10 + d[i];
218. }
219. return ans;
220.}
221.
222.int main(){
223. pre();
224. LL T;
225. cin >> T;
226. while(T --) {
227. LL L, R; cin >> L >> R;
228. cout << MOD( gao(R)-gao(L-1) ) <<endl;
229. /*
230. LL ans = 0;
231. for(LL x = L; x <= R; ++ x) if(!ok(x))
232. update(ans, mul(x,x,P));
233. cout << ans << endl;*/
234. }
235. return 0;
236.}
2.SGU 258
problem
一个2*n位数,前n位数各位数和与后n位数各位数和相等,是lucky数。一个2*n位数,改变一个数字后,依然是2*n位(改前改后都没有前导零),并且是lucky数,则改之前的数称之为近似lucky数。求[l, r] 区间内,有多少近似lucky数。
think
dp[枚举到那一位][这个数是2*n位数][sum(前n位各位数和-后n位数各位数和)][more(最多增加)][less(最多减少)]
sum可能<0 所以给他都+45
more = max(9-前n位某个数, 后n位某个数)
less = max(首位-1, 前n位且非首位的某个数, 9-后n位某个数) //因为首位不能变成0 所以首位-1
答案是sum!=45(是0 的话就不用变了) && sum+more >=45 && sum-less<=45
code
[cpp] view plaincopyprint?
1.const int M = 45;
2.int f[10][10][91][10][10];
3.int b[11];
4.
5.int dfs(int pos, int N, int sum, int more, int less, int e){
6. if(pos==0){
7. return sum!=M && sum+more>=M && sum-less<=M;
8. }
9. if(!e && ~f[pos][N][sum][more][less]) return f[pos][N][sum][more][less];
10. int ans = 0;
11. int u = e?b[pos]:9;
12. int d = pos==N?1:0;
13. for(; d<=u; d++){
14. int ss = pos>N/2 ? sum+d : sum-d;
15. int mm = pos>N/2 ? max(more, 9-d) : max(more, d);
16. int ll = pos>N/2 ? max(less, pos==N?d-1:d) : max(less, 9-d);
17. ans += dfs(pos-1, N, ss, mm, ll, e&&d==u);
18. }
19. return e ? ans : f[pos][N][sum][more][less] = ans;
20.}
21.
22.int s(int n){
23. if(n==-1) return 0;
24. int p = 0;
25. while(n){
26. b[++p] = n%10;
27. n/=10;
28. }
29. int ans = 0;
30. for(int i=2; i<=p; i+=2){
31. ans += dfs(i, i, 0+M, 0, 0, i==p);
32. }
33. return ans;
34.}
35.
36.int main(){
37. memset(f, -1, sizeof(f));
38. int a, b;
39. while(~scanf("%d%d", &a, &b)){
40. printf("%d\n", s(b)-s(a-1));
41. }
42. return 0;
43.}
3.URAL 1057
problem
[l, r] 区间内,有多少个数分解成K个不同B的次方。
think
平时写数位DP,习惯把数位按十进制分解,这道题,分解成B进制,就可以了。然后看有多少个数里面有K个1 其他都是0.
code
[cpp] view plaincopyprint?
1.int K, B;
2.int f[50][50];
3.int bit[50];
4.
5.int dfs(int pos, int num, bool e){
6. if(pos==0) return num==K;
7. if(!e && ~f[pos][num]) return f[pos][num];
8. int ans = 0;
9. int u = e?bit[pos]:B-1;
10. for(int d=0; d<=1 && d<=u; d++){
11. ans += dfs(pos-1, num+d, e&&d==u);
12. }
13. return e?ans:f[pos][num]=ans;
14.}
15.
16.int s(int n){
17. int p = 0;
18. while(n){
19. bit[++p] = n%B;
20. n/=B;
21. }
22. return dfs(p, 0, true);
23.}
24.
25.int main(){
26. int n, m;
27. while(~scanf("%d%d%d%d", &n, &m, &K, &B)){
28. memset(f, -1, sizeof(f));
29. printf("%d\n", s(m)-s(n-1));
30. }
31. return 0;
32.}
数位DP
HDU 4734 F(x) (2013成都网络赛,数位DP)
ZOJ 3494 BCD Code(AC自动机+数位DP)
HDU 4507 吉哥系列故事——恨7不成妻(数位DP)
SPOJ 10606. Balanced Numbers (数位DP)
HDU 3709 Balanced Number ZOJ 3416 Balanced Number(数位DP)
HDU 3652 B-number(数位DP)
CF 55D Beautiful numbers(数位DP)
HDU 4352 XHXJ's LIS(数位DP)
POJ 3252 Round Numbers(数位DP)
UESTC 1307 windy数(数位DP)
HDU 2089 不要62(数位DP)
HDU 3555 Bomb(数位DP)