题意:
题意不好说,反正就是你需要构造一个长度为n的序列,并且每个字符的大小最多是前面出现的最大的字符+1,也就是说ABC是被允许的,但是ACB是不被允许的,因为C之前没有出现过B,现在问你字典序第k大的是多少。
题解:
有点像康托展开,因为我们做到第i位的时候需要知道后面能够取到多少种情况,那么我们用dp[i][j]表示当前数为i的时候,后面有j位的情况数。
那么状态转移方程就是
d
p
[
i
]
[
j
]
=
i
∗
d
p
[
i
]
[
j
−
1
]
+
d
p
[
i
+
1
]
[
j
−
1
]
dp[i][j]=i*dp[i][j-1]+dp[i+1][j-1]
dp[i][j]=i∗dp[i][j−1]+dp[i+1][j−1]
因为我们在这一位是1-j的时候,可以当做这一位是j,因为下一位可以最大到第j+1位,也就是说,你后面能取的值的范围是从前面的最大的数决定的,而不是当前的数,当下一位取到i+1的时候,最大数就变成了i+1。
然后我们一位一位做,对于第i位的时候,我们之后能够取到的最大值当然是前面出现的最大的值的情况,也就是说ABCA的时候,下一位能够出现的最大的值是D,所以我们需要维护一个最大值,然后判一下情况数够不够即可。
注意它的情况数最大可以达到4e19+,所以需要用__int128.JAVA的大数会T,可能是不够优秀吧。
#include<bits/stdc++.h>
using namespace std;
__int128 dp[30][30];
int ans[30];
char s[30];
int main()
{
dp[26][1]=26,dp[26][0]=1;
for(int j=25;j;j--)dp[j][1]=j+1,dp[j][0]=1;
for(int i=2;i<=26;i++)
{
for(__int128 j=25;j;j--)
{
dp[j][i]=j*dp[j][i-1]+dp[j+1][i-1];
if(dp[j][i]>5e19)
dp[j][i]=5e19;
}
}
int t,cas=0;
scanf("%d",&t);
while(t--)
{
int n;
__int128 k=0;
scanf("%d%s",&n,s);
int len=strlen(s);
for(int i=0;i<len;i++)
k=k*10+(__int128)(s[i]-'0');
int mx=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=26;j++)
{
mx=max(mx,j);
if(k<=dp[mx][n-i])
{
ans[i]=j;
break;
}
k-=dp[mx][n-i];
}
}
printf("Case #%d: ",++cas);
for(int i=1;i<=n;i++)
printf("%c",ans[i]+'A'-1);
printf("\n");
}
return 0;
}