test20181016 B君的第一题

Posted autoint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了test20181016 B君的第一题相关的知识,希望对你有一定的参考价值。

题意

技术分享图片
技术分享图片

分析

考场爆零做法

考虑位数少的一定更小,高位小的一定更少。

然后计算一定位数下不同数字的个数,然后从高到低依次确定数位。

特例:如果确定的高位的后缀出现了x,那么要把x调整到后缀去,这样一定更优。

然而这样做有问题,有重复的情况,譬如样例1的6666。

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
    T data=0;
    int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==‘-‘)
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=10*data+ch-‘0‘,ch=getchar();
    return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;

const int MAXN=2e3+7;
char X[MAXN],ans[MAXN];
int len;
ll n;

int main()
{
 freopen("python.in","r",stdin);
  freopen("python.out","w",stdout);
    scanf("%s",X+1);
    len=strlen(X+1);
    read(n);
    
    if(n==1)
    {
        puts(X+1);
        return 0;
    }
    n-=1;
    
    int lim;
    for(lim=1;;++lim)
    {
        ll sum=(ll)lim*9*pow(10,lim-1)+pow(10,lim);
        cerr<<"lim="<<lim<<" sum="<<sum<<endl;
        if(n>sum)
            n-=sum;
        else
            break;
    }
    cerr<<"n="<<n<<" lim="<<lim<<endl;
    for(int i=1;i<=lim;++i)
    {
        cerr<<"i="<<i<<endl;
        for(int j=(i==1?1:0);j<=9;++j)
        {
            ll sum=(ll)(lim-i+1)*pow(10,lim-i);
//          cerr<<" j="<<j<<" sum="<<sum<<endl;
            if(n>sum)
                n-=sum;
            else
            {
                ans[i]=j+‘0‘;
                ans[i+1]=0;
                break;
            }
            if(j==9)
                abort();
        }
        cerr<<"ansi="<<ans[i]<<endl;

        if(i>=len&&strcmp(ans+i-len+1,X+1)==0)
        {
//          cerr<<"n="<<n<<endl;
            int j;
            for(j=lim+len;n;--j,n/=10)
            {
                ans[j]=n%10+‘0‘;
            }
            for(;j>i;--j)
            {
                ans[j]=‘0‘;
            }
            puts(ans+1);
            return 0;
        }
    }
    printf("%s%s",ans+1,X+1);
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}

标解

毕姥爷太强了。

匹配字符串,考虑自动机上dp。

构造对x的KMP自动机,只是末尾位置的trans均指向末尾。

那么如果数字中出现过x,自动机上跑出来一定在末尾位置。

(f(i,j))表示从状态j走i步走到末尾位置上的方案数。写出转移方程:
[ f(i,j)=sum_{k=0}^{9}f(i-1, extrm{trans}(j,k)) ]
边界条件(f(0, extrm{maxstate})=1)

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
    T data=0;
    int w=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==‘-‘)
            w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=10*data+ch-‘0‘,ch=getchar();
    return x=data*w;
}
typedef long long ll;
typedef unsigned long long ull;
const ll INF=(1ULL << 63) - 1;

const int MAXN=2e3+20;
char s[MAXN];
ll n;

int nx[MAXN];
int trans[MAXN][MAXN];

ull f[MAXN][MAXN];


int main()
{
  freopen("python.in","r",stdin);
  freopen("python.out","w",stdout);
    scanf("%s",s+1);
    int l=strlen(s+1);
    
    // 处理next数组 
    int p=0;
    for(int i=2;i<=l;++i)
    {
        while(p && s[p+1] != s[i])
        {
            p = nx[p];
        }
        if(s[p+1] == s[i])
        {
            ++p;
        }
        nx[i]=p;
    }
    
    for(int i=0;i<l;++i) // 处理trans转移数组 
        for(int j=0;j<10;++j)
        {
            p = i;
            while(p && s[p + 1] != j + ‘0‘)
            {
                p = nx[p];
            }
            if(s[p + 1] == j + ‘0‘)
            {
                ++p;
            }
            trans[i][j] = p;
        }
    for(int i=0;i<10;++i)
    {
        trans[l][i]=l;
    }
    
    f[0][l]=1;
    for(int i=1;i<2015;++i) // 2015是估计值 
        for(int j=0;j<=l;++j)
            for(int k=0;k<10;++k)
            {
                f[i][j] += f[i-1][trans[j][k]];
                if(f[i][j] > INF)
                {
                    f[i][j] = INF;
                }
            }
    
    read(n);
    int lim;
    for(lim=1;lim < 2015 && f[lim][0] < n;++lim); 
    p=0;
    for(int i=lim,j;i;--i)
    {
        for(j=0;j<10;++j)
        {
            if(n > f[i-1][trans[p][j]])
                n -=  f[i-1][trans[p][j]];
            else
                break;
        }
        printf("%d",j);
        p = trans[p][j];
    }
    puts("");
//  fclose(stdin);
//  fclose(stdout);
    return 0;
}



以上是关于test20181016 B君的第一题的主要内容,如果未能解决你的问题,请参考以下文章

test20181020 B君的第一题

test20181018 B君的第三题

test20181019 B君的第三题

test20181017 B君的第二题

[20181016][模拟赛]

B君的第九题