zoukankan      html  css  js  c++  java
  • 【XSY2344】K-th String

    Description
    Alice有 n(n≤26) 张牌,牌上分别标有前 n 个英文小写字母。例如,如果 n=3 ,则Alice有3张牌,分别标有"a", "b", "c" 。Alice可以通过排列这些卡牌来构造字符串 t 。考虑字符串 t 的所有子串(共 n(n+1)2 个),按照字典序从小到大排名第 k 的子串为 s 。现在,给你正整数 n,k 和字符串 s ,问有多少种可能的字符串 t 。将答案对 109+7 取模。

    例如: 当 n=3,t="cab" 时,排序后的子串为"a", "ab", "b", "ca", "cab", "cab",排名第3的子串为"b"。当 n=3,k=3,s="b" 时 ,则 t 可能为"cab"或"bac" ,故答案为2种。

    Input
    第一行两个整数 (n,k(1≤n≤26,1≤k≤n(n+1)/2))

    第二行一个字符串 s ,s 中仅包含前 n 个字母,且 s 中的字母两两不同。

    Output

    输出一行表示答案。将答案对 109+7 取模。

    Sample Input

    3 3
    b
    

    Sample Output

    2

    HINT

    数据范围与约定

    对于30%的数据, (1≤n≤8)

    对于所有数据, (1≤n≤26)

    想象一下DP

    我们先枚举s串

    (dp[i][j][k][l])表示前(i)个字母中,有(j)个比(s[1])小,他们对答案的贡献为(k)(添加这个节点后会有多少个新的小于(s[1])的串),(l=0或1),表示现在所取的子串中,有没有s这个串的方案数。

    三种情况状态转移:

    1.不取(i)这个点: dp[i+1][j][kk][l]=dp[i+1][j][kk][l]+dp[i][j][kk][l]

    2.取(i)这个点:

    一个点的贡献就是包含这个点在内,剩余子串的长度。

    dp[i+1][j+1][kk+n-i][l]=dp[i+1][j+1][kk+n-i][l]+dp[i][j][kk][l]

    3.直接取整个s串(前提:之前没取过s):

    直接取s串的贡献就是对s串中的每一个点都求贡献

    if(i+len<=n&&!l)
     {
              dp[i+len][j][kk+(n-i)*sum1-sum][1]=(dp[i+len][j][kk+(n-i)*sum1-sum][1]+dp[i][j][kk][l])%mod;
     }
    

    最后统计答案:

    因为在s串之前的字母的每一种排列都符合要求,所以答案要乘上排列的情况数。

    s串之后的字母同理。

    代码:

    #include<bits/stdc++.h>
    #define mod 1000000007
    using namespace std;
    int n,k,dp[27][27][3050][2],sum,sum1;
    char ch[27];
    int main()
    {
        scanf("%d%d%s",&n,&k,ch+1);
        int len=strlen(ch+1);
        k-=len;
        if(k<0)
        {
            puts("0");
            return 0;
        }
        int num=ch[1]-'a'+1;
        for(int i=1;i<=len;i++)
        {
            if(ch[i]<=ch[1])
            {
                num--;
                if(ch[i]!=ch[1])
                {
                    sum=sum+i-1;
                    sum1++;//在s串之内的小于s[1]的字母的个数
                }
            }
        }
        dp[0][0][0][0]=1;
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                for(int kk=0;kk<=n*(n+1)/2;kk++)
                {
                    for(int l=0;l<=1;l++)
                    {
                        dp[i+1][j][kk][l]=(dp[i+1][j][kk][l]+dp[i][j][kk][l])%mod;//不取
                        dp[i+1][j+1][kk+n-i][l]=(dp[i+1][j+1][kk+n-i][l]+dp[i][j][kk][l])%mod;//取
                        if(i+len<=n&&!l)
                        {
                            dp[i+len][j][kk+(n-i)*sum1-sum][1]=(dp[i+len][j][kk+(n-i)*sum1-sum][1]+dp[i][j][kk][l])%mod;//整个s串
                        }
                    }
                }
            }
        }
        long long ans=dp[n][num][k][1];//答案的一种
        for(int i=1;i<=num;i++)//乘上头和尾的排列数
        {
            ans=(ans*i)%mod;
        }
        for(int i=1;i<=n-num-len;i++)
        {
            ans=(ans*i)%mod;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    CF961E Tufurama 主席树
    [BZOJ3638 && BZOJ3272]带修区间不相交最大K子段和(线段树模拟费用流)
    [BZOJ5294][BJOI2018]二进制(线段树)
    [BZOJ5293][BJOI2018]求和(倍增)
    [BZOJ5306][HAOI2018]染色(容斥+FFT)
    [BZOJ5303][HAOI2018]反色游戏(Tarjan)
    [CF1053C]Putting Boxes Together(线段树)
    整体二分
    JSOI2018R2题解
    LCT维护子树信息
  • 原文地址:https://www.cnblogs.com/2017gdgzoi44/p/11385423.html
Copyright © 2011-2022 走看看