题目大意
给出整数k和t,需要产生一个满足以下要求的第k个十六进制数
即十六进制数每一位上的数出现的次数不超过t
首先我们先这样考虑,如果给你了0~f每个数字可以使用的次数num[i],如何求长度为L且满足要求的十六进制数有多少个
dp[i][l]表示使用了前i个数字,已经将L的空位填上了l个的数有多少个
转移方程 dp[i][l] = sigma(dp[i-1][l-j]*C[len-l+j[j]) 其中j是枚举填新的数的个数,C是组合数(选出j个空位填上新数)
有了这个dp后,现在的问题就变成了找出第k个数
首先先确定位数,枚举位数,然后就可以找到第k个数的位数是多少
然后对从最高位开始枚举,确定每一位应该是多少
最后就可以得出答案
#include <iostream> #include <cstring> #include <cstdio> using namespace std; typedef long long LL; const int maxl = 200; LL C[maxl][maxl], dp[16][maxl]; int num[16], t; LL k; void prepare() { for(int i = 0; i < maxl; i++) C[i][0] = 1; for(int i = 1; i < maxl; i++) for(int j = 1; j <= i; j++) C[i][j] = C[i-1][j] + C[i-1][j-1]; } LL solve(int len) { memset(dp, 0, sizeof(dp)); for(int i = 0; i <= num[0]; i++) dp[0][i] = C[len][i]; for(int i = 1; i < 16; i++) for(int l = 0; l <= len; l++) for(int j = 0; j <= min(num[i], l); j++) dp[i][l] += dp[i-1][l-j]*C[len-l+j][j]; return dp[15][len]; } void print(int j) { if(j < 10) cout<<j; else cout<<(char)(j+'a'-10); } int main() { prepare(); cin>>k>>t; for(int i = 0; i < 16; i++) num[i] = t; int len = 1; for(;; len++) { LL tmp = 0; if(len == 1) tmp = 15; else for(int j = 1; j < 16; j++) { num[j]--; tmp += solve(len-1); num[j]++; } if(k > tmp) k -= tmp; else break; } for(int i = len; i > 0; i--) { if(i == 1) { for(int j = 0; j < 16; j++) { if(j == 0 && len == 1) continue; if(num[j] != 0) k--; if(k == 0) { print(j); break; } } break; } for(int j = 0; j < 16; j++) { if(i == len && j == 0) continue; num[j]--; LL tmp = solve(i-1); if(k > tmp) k -= tmp; else { print(j); break; } num[j]++; } } }