CF747F Igor and Interesting Numbers

我佛了,這CF竟然沒有官方題解。ide

題意:給定k,t,求第k小的16進制數,知足每一個數碼的出現次數不超過t。函數

解:spa

每一個數都有個出現次數限制,搞不倒。一開始想到了排序hash數位DP,不過寫了寫以爲不勝其煩,就棄療了。設計

可是思考一下,若是咱們知道了每一個數的出現次數和數的位數,那麼一次普通DP就可以求出方案數。rest

因此咱們暴力作屢次這種普通DP便可......code

具體來講,分爲帶前導0和不帶前導0兩個DP函數。blog

首先枚舉數的長度,計算不帶前導0的個數。若是不到k就減去。排序

而後知道了長度,再一位一位的肯定。在每一位上枚舉放哪一個數碼。若是方案數不足就減去這麼多。string

對於那個DP函數,狀態設計f[i][j]表示用前i個數碼放j位的數的方案數。轉移就是hash

f[i][j] = f[i - 1][j - k] * C(j, k),表示在j個位置中選出k個放數碼i,剩下的放前面的數碼,前面的數碼相對位置不變。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 typedef long long LL;
 6 const int N = 200, B = 16;
 7 
 8 LL f[17][N], C[N][N], choose[N];
 9 int rest[B];
10 
11 inline LL DP(int n) { // with leading zero
12     if(n <= 0) {
13         return 1;
14     }
15     memset(f, 0, sizeof(f));
16     for(int i = 1; i <= B; i++) {
17         f[i - 1][0] = 1;
18         for(int j = 1; j <= n; j++) {
19             for(int k = 0; k <= rest[i - 1] && k <= j; k++) {
20                 f[i][j] += f[i - 1][j - k] * C[j][k];
21             }
22         }
23     }
24 
25     return f[16][n];
26 }
27 
28 inline LL DP1(int n) { // no leading zero
29     LL ans = 0;
30     for(int i = 1; i < B; i++) {
31         if(rest[i]) {
32             rest[i]--;
33             ans += DP(n - 1);
34             rest[i]++;
35         }
36     }
37     return ans;
38 }
39 
40 int main() {
41     for(int i = 1; i < 200; i++) {
42         C[i][0] = C[i][i] = 1;
43         for(int j = 1; j < i; j++) {
44             C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
45         }
46     }
47     int t;
48     LL k;
49     scanf("%lld%d", &k, &t);
50     int len;
51     for(len = 1; ; len++) {
52         for(int i = 0; i < B; i++) {
53             rest[i] = t;
54         }
55         LL temp = DP1(len);
56         if(temp >= k) {
57             break;
58         }
59         k -= temp;
60     }
61 
62     for(int i = 0; i < B; i++) {
63         rest[i] = t;
64     }
65     for(int i = len; i >= 1; i--) {
66         for(int j = (i == len); j < B; j++) {
67             if(!rest[j]) {
68                 continue;
69             }
70             rest[j]--;
71             LL temp = DP(i - 1);
72             if(temp < k) {
73                 k -= temp;
74                 rest[j]++;
75             }
76             else {
77                 choose[i] = j;
78                 break;
79             }
80         }
81     }
82 
83     for(int i = len; i >= 1; i--) {
84         if(choose[i] < 10) {
85             printf("%d", choose[i]);
86         }
87         else {
88             putchar('a' + choose[i] - 10);
89         }
90     }
91     return 0;
92 }
AC代碼
相關文章
相關標籤/搜索