[2019上海网络赛F题]Rhyme scheme
题意,求出合法的长度为n的字典序第k小字符串,合法的定义为除了最后一位,每一位的取值范围为'A'到'A'+pos-1,而最后一位的取值范围'A'到当前字符串最大值+1。
队友tql,Orz
一开始就想爆搜,但是不知道如何判断当前位为X时的合法字符串个数。然后队友就莽过去了Orz。
大致做法就是类似数位dp的方式预处理出第i位为j时有多少合法字符串,这样求答案时就可以直接判断能否满足第k个,满足就继续搜下去,不满足就k-dp[i][j]。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef __int128 ll; 4 const int mod = 1e9 + 7; 5 ll dp[30][30]; 6 int ans[30]; 7 int n; ll k; 8 ll read() { 9 ll x = 0, f = 1; 10 char c = getchar(); 11 for (; !isdigit(c); c = getchar()) if (c == '-') f = -1; 12 for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; 13 return x * f; 14 } 15 ll dfs(int pos, int limit) { 16 if (pos == n + 1)return 1; 17 if (dp[pos][limit] != -1)return dp[pos][limit]; 18 ll sum = 0; 19 for (int i = 0; i <= limit; i++) 20 sum += dfs(pos + 1, max(limit, i + 1)); 21 dp[pos][limit] = sum; 22 return dp[pos][limit]; 23 } 24 void solve(int pos, int limit, ll k) { 25 if (pos == n) { 26 for (int i = 0; i < n; i++) 27 printf("%c", ans[i] + 'A'); 28 printf("\n"); 29 return; 30 } 31 for (int i = 0; i <= limit; i++) { 32 ll lim = max(limit, i + 1); 33 if (dp[pos + 1][lim] >= k) { 34 ans[pos] = i; 35 solve(pos + 1, lim, k); 36 break; 37 } 38 else 39 k -= dp[pos + 1][lim]; 40 } 41 } 42 int main() { 43 int t, cnt = 1; 44 scanf("%d", &t); 45 while (t--) { 46 scanf("%d", &n); 47 k = read(); 48 for (int i = 0; i <= 26; i++) 49 for (int j = 0; j <= 26; j++) 50 dp[i][j] = -1; 51 for (int i = 0; i < 30; ++i) dp[n][i] = 1; 52 dfs(0, 0); 53 printf("Case #%d: ", cnt++); 54 solve(0, 0, k); 55 } 56 }