Educational Codeforces Round 119 (Rated for Div. 2) C 组合数学 构造字符串 字典序 爆longlong小技巧

73 篇文章 2 订阅
71 篇文章 1 订阅

题目

给你一个长度为 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 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值