题目
给你一个长度为 n 只含a和*的字符串。
其中 * 可以变成 0 到 k 长度的只含 b 的字符串 。
在原字符串可以改变的所有情况中求出
字典序第x小的字符串。
n < 2000
k < 2000
x < 1e18
题解思路
肯定是从后面的星来往前加b来维持字典序最小。
我们考虑每两个a之间的*能在字典序的多少位。
并且字典序还受到之前的数的影响。
可以在首位末位加入a来更好的控制b的数量。
即下面这个图的规律。
我们使用这个乘法法则构造出每两个a之间位置满位的最大字典序。
可以发现,每个a都有倍数关系,这里就可以通过取余来处理出下一个数可能放的情况。
再根据数 - 1 再除后一个 a 的结果即是在这个位置放多少个b
这题期间极大可能会爆longlong(大数不断相乘)。
这里用了标记long double 大一点的特性来标记处理 爆longlong的地方。
AC代码
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200100;
char s[N] ;
int n ;
long long cnt[N] ;
long long fx[N] ;
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T ;
cin >> T ;
while ( T-- )
{
long long n , k , x ;
memset( cnt , 0 , sizeof(cnt) ) ;
memset( fx , 0 , sizeof(fx) ) ;
cin >> n >> k >> x ;
cin >> s+1 ;
s[0] = 'a' , s[n+1] = 'a' ;
cnt[n+1] = 1 ;
for (int i = n ; i >= 0 ; i--)
{
if ( s[i] == 'a')
{
for (int j = i+1 ; j <= n+1 ; j++ )
{
if ( s[j] == 'a' )
{
if ( cnt[j] != -1 && 1.0*cnt[j]*((j-i-1)*k + 1 ) <= 1e18 )
{
cnt[i] = cnt[j]*((j-i-1)*k + 1 ) ;
//cout << cnt[i] << " " << i << "\n" ;
}else
cnt[i] = -1 ;
break;
}
}
}
}
for (int i = 0 ; i <= n ; i++ )
{
if ( s[i] == 'a')
{
if ( i != 0 )
cout << "a" ;
for (int j = i+1 ; j <= n +1 ; j ++ )
{
if ( s[j] == 'a' )
{
if ( cnt[j] != -1 )
{
int num = (x-1)/cnt[j] ;
for ( int k = 1 ; k <= num ; k++ )
cout << "b" ;
x = (x-1)%cnt[j] + 1 ;
}
break;
}
}
}
}
cout << "\n" ;
}
return 0 ;
}