【数位dp】

hdu5787

问:L ~ R有多少个数是K-wolf Number?其中,K-wolf Number的定义是这个数在十进制下,任意相邻的K个字符没有相同的。

dp[i][j][k]表示有i个空位可填,其中不能填j这个数字中所含有的字符,k = true(表示j有前导0)时,还不能填0.

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 #define pii pair<int, int>
 4 #define mp make_pair
 5 #define fi first
 6 #define se second
 7 using namespace std;
 8 const int N = 1e5+5;
 9 ll l, r;
10 int k;
11 ll dp[20][100001][2];
12 int temp[20], tot;
13 
14 ll dfs(int pos, int w, bool tag, bool flag){
15 
16     if(!pos) return 1;
17     if(flag&&~dp[pos][w][tag]) return dp[pos][w][tag];
18     int Max = flag? 9: temp[pos];
19 
20     int ret[5], sum = 0, tmp = w;
21     while(tmp)
22         ret[sum++] = tmp%10, tmp /= 10;
23     if(tag) ret[sum++] = 0;
24 
25     ll ans = 0;
26     for(int i = 0; i <= Max; i++){
27         bool tag2 = true;
28         for(int j = 0; j < sum&&j < k-1; j++)
29             if(ret[j] == i) tag2 = false;
30         if(tag2){
31             tmp = 0;
32             int first = min(k-2, sum-1);
33             for(int j = first; j >= 0; j--)
34                 tmp = tmp*10+ret[j];
35             bool tag3 = false;
36             if(first >= 0&&ret[first] == 0) tag3 = true;
37             ans += dfs(pos-1, tmp*10+i, tag3, flag||i != Max);
38         }
39     }
40     if(flag) dp[pos][w][tag] = ans;
41     //printf("dfs %d, %d, %d, %d %lld
", pos, w, tag*1, flag*1, ans);
42     return ans;
43 }
44 
45 ll solve(ll x){
46     tot = 0;
47     while(x){
48         temp[++tot] = x%10;
49         x /= 10;
50     }
51     return dfs(tot, 0, 0, 0);
52 }
53 int main(){
54     while(cin >> l >> r >> k){
55         memset(dp, -1, sizeof(dp));
56         ll ans = solve(r)-solve(l-1);
57         cout << ans << endl;
58     }
59     return 0;
60 }
View Code

hdu3652

问:1~n有多少个数含有连续的“13”并且能被13整除。

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 int dp[11][13][10][2];
 5 int temp[20], tot;
 6 
 7 int dfs(int pos, int mod, int pre, bool tag13, bool flag){
 8     if(!pos)
 9         return (mod == 0&&tag13);
10     if(flag&&~dp[pos][mod][pre][tag13]) return dp[pos][mod][pre][tag13];
11     int Max = flag? 9: temp[pos];
12 
13     int ans = 0;
14     for(int i = 0; i <= Max; i++)
15         ans += dfs(pos-1, (mod*10+i)%13, i, tag13||(pre == 1&&i == 3), flag||i != Max);
16     if(flag) dp[pos][mod][pre][tag13] = ans;
17     return ans;
18 }
19 
20 int solve(int x){
21     tot = 0;
22     while(x){
23         temp[++tot] = x%10;
24         x /= 10;
25     }
26     return dfs(tot, 0, 0, 0, 0);
27 }
28 int main(){
29     memset(dp, -1, sizeof(dp));
30     int n;
31     while(cin >> n){
32         ll ans = solve(n)-solve(0);
33         cout << ans << endl;
34     }
35     return 0;
36 }
View Code

hdu4734

问:0~B中有多少数字各个数位权值和小于 f(A).

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int lim;
 4 int dp[10][9<<9];
 5 int temp[20], tot;
 6 int dfs(int pos, int sum, bool flag){
 7     if(!pos)
 8         return 1;
 9     if(flag&&~dp[pos][sum]) return dp[pos][sum];
10     int Max = flag? 9: temp[pos];
11     int ans = 0;
12     for(int i = 0; i <= Max; i++){
13         if( (i<<(pos-1)) <= sum )
14             ans += dfs(pos-1, sum-(i << (pos-1)), flag||i != Max);
15     }
16     if(flag) dp[pos][sum] = ans;
17     return ans;
18 }
19 
20 int solve(int x){
21     tot = 0;
22     while(x){
23         temp[++tot] = x%10;
24         x /= 10;
25     }
26     return dfs(tot, lim, 0);
27 }
28 int main(){
29     memset(dp, -1, sizeof(dp));
30     int t, a, b, ca = 1; scanf("%d", &t);
31     while(t--){
32         scanf("%d%d", &a, &b);
33         lim = 0;
34         for(int i = 0; a; i++){
35             lim += (a%10)<<i;
36             a /= 10;
37         }
38         // lim < (9*2^9)
39         int ans = solve(b)-solve(0)+1;
40         printf("Case #%d: %d
", ca++, ans);
41     }
42     return 0;
43 }
View Code

hdu4507

问:l~r中与7无关的数的平方和。与7无关的数定义为:不含数字7,不是7的倍数,且各位数和不是7的倍数。

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int P = 1e9+7;
 5 ll f[22];
 6 ll l, r;
 7 struct p{
 8     ll x, y, z;
 9     p(){}
10     p(ll x, ll y, ll z):x(x), y(y), z(z){}
11 };
12 p dp[22][7][7];
13 int temp[22], tot;
14 void ad(ll& a, ll b){
15     a += b;
16     if(a < 0) a = a%P+P;
17     if(a >= P) a %= P;
18 }
19 p dfs(int pos, int add, int mod, bool flag){
20     if(!pos)
21         return p(add&&mod, 0, 0);
22     if(flag&&dp[pos][add][mod].x != -1) return dp[pos][add][mod];
23 
24     int Max = flag? 9: temp[pos];
25     ll ans = 0, sum = 0, sum2 = 0;
26     for(int i = 0; i <= Max; i++) if(i != 7){
27         p ret = dfs(pos-1, (add+i)%7, (mod*10+i)%7, flag||i != Max);
28         //i*10^(pos-1)+x
29         ll tmp = f[pos-1]*i%P;
30         ans += ret.x;
31         ad(sum, (ret.y+ret.x%P*tmp)%P);
32         ad(sum2, (ret.x%P*tmp%P*tmp%P+2*tmp*ret.y%P+ret.z)%P);
33     }
34     if(flag) dp[pos][add][mod] = p(ans, sum, sum2);
35     //printf("pos %d, add %d, mod %d, flag %d: %lld %lld %lld
", pos, add, mod, flag*1, ans, sum, sum2);
36     return p(ans, sum, sum2);
37 }
38 
39 
40 ll solve(ll x){
41     tot = 0;
42     while(x){
43         temp[++tot] = x%10;
44         x /= 10;
45     }
46     return dfs(tot, 0, 0, 0).z;
47 }
48 int main(){
49     f[0] = 1;
50     for(int i = 1; i <= 20; i++) f[i] = f[i-1]*10%P;
51     memset(dp, -1, sizeof(dp));
52     int t, a, b, ca = 1; scanf("%d", &t);
53     while(t--){
54         scanf("%lld%lld", &l, &r);
55         ll ans = solve(r)-solve(l-1);
56         ans %= P;
57         if(ans < 0) ans += P;
58         printf("%lld
", ans);
59     }
60     return 0;
61 }
View Code

hdu3709

问:l~r中有多少数字是平衡数? 平衡数的定义为能在数字中找到某一个点作为支点使左边的力矩和右边的力矩相等?如4139,以3为支点时左边是4*2+1*1 = 9, 右边是9*1 = 9.

 1 #include <bits/stdc++.h>
 2 #define ll unsigned long long
 3 using namespace std;
 4 ll dp[22][22][600+600];
 5 ll l, r;
 6 
 7 int temp[22], tot;
 8 
 9 ll dfs(int pos, bool pre, int pivot, int now, bool flag){
10     if(!pos)
11         return now == 0;
12     if(flag&&~dp[pos][pivot][600+now]) return dp[pos][pivot][600+now];
13 
14     int Max = flag? 9: temp[pos];
15     ll ans = 0;
16     for(int i = 0; i <= Max; i++){
17         if(!pre&&pivot == pos&&!i) continue ;
18         int ret = now+(pos-pivot)*i;
19         if(ret > 550||ret < -550) continue ;
20         ans += dfs(pos-1, pre||i, pivot, ret, flag||i != Max);
21     }
22 
23     if(flag) dp[pos][pivot][600+now] = ans;
24     return ans;
25 }
26 
27 
28 ll solve(ll x){
29     tot = 0;
30     while(x){
31         temp[++tot] = x%10;
32         x /= 10;
33     }
34     ll ans = 0;
35     for(int i = 1; i <= tot; i++)
36         ans += dfs(tot, 0, i, 0, 0);
37     return ans;
38 }
39 int main(){
40     memset(dp, -1, sizeof(dp));
41     int t, ca = 1; scanf("%d", &t);
42     while(t--){
43         cin >> l >> r;
44         ll ans = 0;
45         if(l == 0) l++, ans++;
46         ans += solve(r)-solve(l-1);
47         cout << ans << endl;
48     }
49     return 0;
50 }
View Code

SPOJ Balanced Numbers

问:l~r有多少数字满足奇数数字出现偶数次偶数数字出现奇数次。

 1 #include <bits/stdc++.h>
 2 #define ll unsigned long long
 3 using namespace std;
 4 //3^10 = 59049
 5 ll f[11];
 6 ll dp[22][2][59050];
 7 ll l, r;
 8 
 9 int temp[22], tot;
10 bool judge(int x){
11     bool tag = true;
12     while(x){
13         int ret = x%3;
14         if(ret){
15             ret &= 1;
16             if(ret != tag) return false;
17         }
18         tag = !tag;
19         x /= 3;
20     }
21     return true;
22 }
23 ll dfs(int pos, bool pre, int now, bool flag){//pre == true 1
24 //    printf("%d %d %d %d
", pos, pre*1, now, flag*1);
25     if(!pos)
26         return judge(now);
27     if(flag&&~dp[pos][pre][now]) return dp[pos][pre][now];
28 
29     int Max = flag? 9: temp[pos];
30     ll ans = 0;
31     for(int i = 0; i <= Max; i++){
32         int ret = 0;//3^j
33         if(i||pre){
34             ret = (now/f[i])%3;
35             if(ret < 2) ret = now+f[i];
36             else ret = now-f[i];
37         }
38         ans += dfs(pos-1, pre||i, ret, flag||i != Max);
39     }
40 
41     if(flag) dp[pos][pre][now] = ans;
42     //printf("pos %d, add %d, mod %d, flag %d: %lld %lld %lld
", pos, add, mod, flag*1, ans, sum, sum2);
43     return ans;
44 }
45 
46 
47 ll solve(ll x){
48     tot = 0;
49     while(x){
50         temp[++tot] = x%10;
51         x /= 10;
52     }
53     return dfs(tot, 0, 0, 0);
54 }
55 int main(){
56     f[0] = 1;
57     for(int i = 1; i < 11; i++) f[i] = f[i-1]*3;
58     memset(dp, -1, sizeof(dp));
59     int t, ca = 1; scanf("%d", &t);
60     while(t--){
61         cin >> l >> r;
62         ll ans = solve(r)-solve(l-1);
63         cout << ans << endl;
64     }
65     return 0;
66 }
View Code

hdu4352

问:l~r有多少数字的严格最长上升子序列是k?

题解:状压。dp[还剩i位][k][二进制位下的最长上升子序列]

 1 #include <bits/stdc++.h>
 2 #define ll unsigned long long
 3 using namespace std;
 4 const int N = 1e5+5;
 5 const int P = 1e9+7;
 6 ll dp[11][22][1<<11];
 7 ll l, r, k;
 8 
 9 int temp[22], tot;
10 ll dfs(int pos, int k, int now, bool flag){
11     if(!pos)
12         return __builtin_popcount(now) == k;
13     if(flag&&~dp[k][pos][now]) return dp[k][pos][now];
14 
15     int Max = flag? 9: temp[pos];
16     ll ans = 0;
17     for(int i = 0; i <= Max; i++) {
18         if(!now&&!i) {
19             ans += dfs(pos-1, k, now, flag||i != Max);
20             continue ;
21         }
22         int ret = now;
23         if( !((ret>>i)&1) ){//ret[i] : 0
24             if( ret>>i ){
25                 for(int j = i+1; j; j++)
26                     if((ret>>j)&1){
27                         ret ^= 1<<j;
28                         break;
29                     }
30             }
31             ret |= 1<<i;
32         }
33         ans += dfs(pos-1, k, ret, flag||i != Max);
34     }
35 
36     if(flag) dp[k][pos][now] = ans;
37     return ans;
38 }
39 
40 
41 ll solve(ll x){
42     tot = 0;
43     while(x){
44         temp[++tot] = x%10;
45         x /= 10;
46     }
47     return dfs(tot, k, 0, 0);
48 }
49 int main(){
50     memset(dp, -1, sizeof(dp));
51 
52     int t, ca = 1; scanf("%d", &t);
53     while(t--){
54         cin >> l >> r >> k;
55         ll ans = solve(r)-solve(l-1);
56         printf("Case #%d: ", ca++);
57         cout << ans << endl;
58     }
59     return 0;
60 }
View Code

Codeforces 55D

问:l~r有多少数字可以被各个位上的非0数字整除?

 1 #include <bits/stdc++.h>
 2 #define ll unsigned long long
 3 using namespace std;
 4 const int P = 2520;
 5 ll dp[20][1<<7][2550];
 6 ll l, r;
 7 int w[10][4] = {
 8     {0, 0, 0, 0},
 9     {0, 0, 0, 0},
10     {1, 0, 0, 0},
11     {0, 1, 0, 0},
12     {2, 0, 0, 0},
13     {0, 0, 1, 0},
14     {1, 1, 0, 0},
15     {0, 0, 0, 1},
16     {3, 0, 0, 0},
17     {0, 2, 0, 0}
18 };
19 int temp[22], tot;
20 ll dfs(int pos, int k, int now, bool flag){
21     //printf("%d %d %d
", pos, k, now);
22     if(!pos){
23         int a2 = k&3, a3 = (k>>2)&3, a5 = (k>>4)&1, a7 = k>>5;
24         if(a2 == 1&&now%2) return 0;
25         if(a2 == 2&&now%4) return 0;
26         if(a2 == 3&&now%8) return 0;
27         if(a3 == 1&&now%3) return 0;
28         if(a3 == 2&&now%9) return 0;
29         if(a5&&now%5) return 0;
30         if(a7&&now%7) return 0;
31         return 1;
32     }
33     if(flag&&~dp[pos][k][now]) return dp[pos][k][now];
34 
35     int Max = flag? 9: temp[pos];
36     ll ans = 0;
37     int ret[4], ret2[4];
38     ret[0] = k&3, ret[1] = (k>>2)&3, ret[2] = (k>>4)&1, ret[3] = k>>5;
39     for(int i = 0; i <= Max; i++) {
40         for(int j = 0; j < 4; j++)
41             ret2[j] = max(ret[j], w[i][j]);
42         int r = ret2[0]+(ret2[1]<<2)+(ret2[2]<<4)+(ret2[3]<<5);
43         ans += dfs(pos-1, r, (now*10+i)%2520, flag||i != Max);
44     }
45 
46     if(flag) dp[pos][k][now] = ans;
47     //printf("%d %d %d: %lld
", pos, k, now, ans);
48     return ans;
49 }
50 
51 
52 ll solve(ll x){
53     tot = 0;
54     while(x){
55         temp[++tot] = x%10;
56         x /= 10;
57     }
58     return dfs(tot, 0, 0, 0);
59 }
60 int main(){
61     memset(dp, -1, sizeof(dp));
62     int t, ca = 1; scanf("%d", &t);
63     while(t--){
64         cin >> l >> r;
65         ll ans = solve(r)-solve(l-1);
66         cout << ans << endl;
67     }
68     return 0;
69 }
View Code
原文地址:https://www.cnblogs.com/dirge/p/6028794.html