• 2019上海网络赛 F. Rhyme scheme 普通dp


    Rhyme scheme

    Problem Describe

    A rhyme scheme is the pattern of rhymes at the end of each line of a poem or song. It is usually referred to by using letters to indicate which lines rhyme; lines designated with the same letter all rhyme with each other.

    e.g., the following "poem'' of 4 lines has an associated rhyme scheme "ABBA''

    1 —— 99 bugs in the code A

    2 —— Fix one line B

    3 —— Should be fine B

    4 —— 100 bugs in the code A

    This essentially means that line 1 and 4 rhyme together and line 2 and 3 rhyme together.

    The number of different possible rhyme schemes for an nnn-line poem is given by the Bell numbers. For example, (B_3 = 5), it means there are five rhyme schemes for a three-line poem: AAA, AAB, ABA, ABB, and ABC.

    The question is to output the (k)-th rhyme scheme in alphabetical order for a poem of nnn lines.For example: the first rhyme scheme of a three-line poem is "AAA'', the fourth rhyme scheme of a three-line poem is ABB''.

    InputFile

    The first line of the input gives the number of test cases, T((1 leq T leq 10000)). T test cases follow.

    Each test case contains a line with two integers nnn and (k).

    (1 leq n leq 26, 1 leq k leq B_n) ((B_n) is the (n)-th of Bell numbers)

    OutputFile

    For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the rhyme scheme contains uppercase letters.

    样例输入

    7
    1 1
    2 1
    3 1
    3 2
    3 3
    3 4
    3 5

    样例输出

    Case #1: A
    Case #2: AA
    Case #3: AAA
    Case #4: AAB
    Case #5: ABA
    Case #6: ABB
    Case #7: ABC

    题意

    就是求第k个排列,但是有些排列是重复的,比如AAB和AAC算一种(如果不能理解可以看看题目)

    题解

    这里其实都是瞎扯

    我一开始想的就是求当(n=26)时总共有多少种不同的排列。然后想了个dp,然后想用递归,每次确定第一位选什么,然后再做后面的。但是我的两个队友听到我的想法都说不懂,然后看别的题了,然后我就一个人想啊想,一路上遇到了许多问题,还好最后还是解决了。

    首先我们需要发现一些性质:

    1. 长度为n的字符串,只会用n种字母,这个性质很明显也很重要。
    2. 每个字符串的种类会因为前面的不同而变化,比如aaa后面接两种字母a、b,而aab后面可以接a、b、c.
    3. 我们只要前面字符串用过的种类数以及后面字符串的长度,后面字符串的种类也就确定了
    4. 有人可能会觉得要是前面字符串用a、b、c和用a、b、d都是三种但是不一样,但是a、b、d是不会出现的(可以结合第一点想想)

    以上的性质可不是那么容易就能明白的,所以请结合具体例子仔细想想。

    题解正式开始

    先说说dp的预处理吧:

    • (Large 定义:) (dp[n][m] [h])用m种字母组成长度为m的字符串,字符串前面接一个用了h种字母的字符串(不在乎前面的字符串的长度)。
    • (Large 转移方程:)(dp[n][m][h]=dp[n][m-1][h]+dp[n][m][h]*m)
    • (Large初始化:) (dp[0][0][h]=h)

    然后定义(f[n][h])(sum(dp[n][1..m][h])),之后开始从第一位开始枚举,字符从小到大的开始枚举。

    我这里是用的的递归,void find(n,k,h)​ 即前面的字符串用了h种字符,求接下n个字母组成的字符串的第k个排列。

    然后一层层做下去,直到(n=0) 结束

    ps:会爆int64,于是我用了int128;还有就是我并没有讲的太清楚,只是讲了主要思想和具体实现,至于怎么想到的,那我也不清楚。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    template<typename T>void read(T&x)
    {
        ll k=0; char c=getchar();
        x=0;
        while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
        if (c==EOF)exit(0);
        while(isdigit(c))x=x*10+c-'0',c=getchar();
        x=k?-x:x;
    }
    void read_char(char &c)
    {while(!isalpha(c=getchar())&&c!=EOF);}
    
    __int128 f[28][28],dp[28][28][28],k;
    ll n;
    void init()//预处理部分
    {
        for(ll i=0;i<=26;i++)dp[0][i][i]=1;
        for(ll h=0;h<=26;h++)
            for(ll i=1;i<=26;i++)
                for(ll j=1;j<=26;j++)
                    dp[i][j][h]=dp[i-1][j][h]*j+dp[i-1][j-1][h];
        for(ll h=0;h<=26;h++)
            for(ll i=0;i<=26;i++)
                for(ll j=0;j<=26;j++)
                    f[i][h]+=dp[i][j][h];
    }
    void Find(ll n,__int128 k,ll h)//递归求解
    {
        if (n==0)return;
        ll tp=(k-1)/f[n-1][h];
        if (tp+1<=h)//格外注意h=0的时候
        {
            printf("%c",'A'+(int)(tp));
            Find(n-1,k-tp*f[n-1][h],h);
        }
        else
        {
            printf("%c",'A'+(int)(h));
            Find(n-1,k-h*f[n-1][h],h+1);
        }
    }
    
    void work()
    {
        static int cas=0;
        printf("Case #%d: ",++cas);
        read(n); read(k);
        ll p=0;
        Find(n,k,p);
        if (cas)printf("
    ");
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("aa.in","r",stdin);
    #endif
        init();
        int T;
        read(T);
        while(T--)work();
    }
    
    
  • 相关阅读:
    公输盘
    电脑机器刷BIOS
    八皇后问题的实现
    安装msdn出现的问题及解决
    加密推荐书籍
    C++待解
    atan()与atan2()
    Win32/MFC/COM学习推荐书籍
    C++问题
    windows2000 sp4下载
  • 原文地址:https://www.cnblogs.com/mmmqqdd/p/11523766.html
Copyright © 2020-2023  润新知