zoukankan      html  css  js  c++  java
  • CodeForces 645E Intellectual Inquiry

    $dp$,贪心。

    不得不说这题出的很$6$,解法更$6$。

    首先要会统计一个字符串有多少个本质不同的子序列,有两种$dp$策略:

    ①$dp[i]$表示$[1,i]$内的字符有多少种本质不同的子序列,$dp[i]=2*dp[i-1]-dp[pre[s[i]]-1]$,如果$s[i]$是第一次出现,那么$dp[i]$还需要额外$+1$。$pre[x]$表示字符$x$上一次出现的位置。

    $dp[i]$可以理解为$[1,i-1]$的种类数保留下来,再加上$[1,i-1]$每一种后面加上一个字符$x$,但是这样加多了,因为$[1,pre[x]-1]$区间内的种类加上字符$x$多统计了一次,所以要减掉。最终答案为$dp[n]$。

    ②$dp[i][j]$表示$[1,i]$内的字符,以字符$j$为结尾的本质不同的子序列的个数。

    如果$s[i]≠j$,那么$dp[i][j]=dp[i-1][j]$;否则$dp[i][j] = sumlimits_{p = 1}^k {dp[i - 1][p]}+1$ 。最终答案为:$sumlimits_{p = 1}^k {dp[n][p]}$。

    就这题而言的话,利用方法②很容易理解。

    首先将$t$串的不同子序列的个数统计好,然后就是要再在后面加上$n$个自己构造的字符。观察递推式,会发现每一次是将$sumlimits_{p = 1}^k {dp[i - 1][p]}+1$赋值给一个$dp[i][x]$,其余的$dp[i][y]$保留之前的。因为答案要求最大值,所以肯定是选择将$dp[i-1][x]$最小的重新赋值,其余保留。

    注意到不能直接寻找最小值,因为进行了取模,观察后会发现只要知道哪一个字符最后出现的位置最早就可以了,因为那个$dp$是递增的,越早出现的$dp$值肯定是越小的。

    #pragma comment(linker, "/STACK:1024000000,1024000000")
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<queue>
    #include<stack>
    #include<bitset>
    #include<iostream>
    using namespace std;
    typedef long long LL;
    const double pi=acos(-1.0),eps=1e-6;
    void File()
    {
        freopen("D:\in.txt","r",stdin);
        freopen("D:\out.txt","w",stdout);
    }
    template <class T>
    inline void read(T &x)
    {
        char c=getchar(); x=0;
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) {x=x*10+c-'0'; c=getchar();}
    }
    
    const int maxn=1000010;
    int n,k,pre[maxn];
    LL f[30],mod=1e9+7;
    char s[maxn];
    
    int main()
    {
        scanf("%d%d",&n,&k);
        scanf("%s",s); int m=strlen(s);
        for(int i=1;i<=m;i++)
        {
            LL sum=0; for(int j=1;j<=k;j++) sum=(sum+f[j])%mod;
            f[s[i-1]-'a'+1]=(sum+1)%mod; pre[s[i-1]-'a'+1]=i;
        }
    
        for(int i=m+1;i<=m+n;i++)
        {
            LL sum=0; for(int j=1;j<=k;j++) sum=(sum+f[j])%mod;
            int mx=3*maxn,mi;
            for(int j=1;j<=k;j++) if(pre[j]<mx) mi=j,mx=pre[j];
            f[mi]=(sum+1)%mod; pre[mi]=i;
        }
    
        LL ans=0;
        for(int i=1;i<=k;i++) ans=(ans+f[i])%mod;
        printf("%lld
    ",(ans+1)%mod);
    
        return 0;
    }
  • 相关阅读:
    js实现继承
    简单原型语法和原型动态性
    js中关于原型的几个方法
    js创建对象的几种方式
    收藏的js学习小例子
    结合 WebService 实现消息 主动推送到客户端
    Exceptionless 本地搭建记录
    EF6 SqlServer 简单例子 和 支持的原生sql例子
    Ubuntu下deb文件及tgz文件安装
    Windows 10家庭版远程桌面连接错误
  • 原文地址:https://www.cnblogs.com/zufezzt/p/5915953.html
Copyright © 2011-2022 走看看