zoukankan      html  css  js  c++  java
  • CF645E Intellectual Inquiry

    洛谷传送门 CF传送门

    正好学校考试考到过一个加强版,写一写。

    Solution

    很明显的DP

    如果 (n=0) ,设 (f_i) 表示到 (i) 位置之前不同的子序列数,可以得到两个转移方程:

    1. (i) 位的字符之前没有出现过,方程就是 (f_i=2 imes f_{i-1}+1)

      意思是有 (i-1) 位时的方案,和 ({i-1}) 位的每一种方案都加第 (i) 位上的字符,还有自己 (1)

    2. (i) 位的字符出现过,方程是 (f_{i}=2 imes f_{i-1}-f_{pre_{a_i}})

      其中 (pre_{a_i}) 是第 (i) 位字符上次出现的位置,这个方程的意思是要减去上次这个字符的贡献

    现在 (n>0) ,因为要最大数量,所以第二个方程中 (f_{pre_{a_i}}) 减的越少越好,那么填的时候按 (pre_{a_i}) 从小到大依次填入最优

    此时的复杂度是 (O(n+m)) ,完全足够通过本题


    但是,如果是 (nleq 10^{18})

    这个时候,上面的方法就完全不行了

    我们发现在填 (n) 的时候,每 (k) 位都算是一个循环节,而 (kleq 26)

    我们可以考虑用一些和 (k) 有关且能快速求出答案的方法——矩阵快速幂

    将矩阵初始化的时候考虑第 (i) 个字符和第 (j) 个字符之间的相互影响即可

    时间复杂度: (O(m+k^3log n))

    注意:运算的时候没加空集,输出答案的时候记得加上

    Code

    #include<bits/stdc++.h>
    #define ll long long
    
    using namespace std;
    const int mod=1e9+7,N=4e6+10;
    int m,k,a[N],vis[N],q[N],cnt,f[N],pre[N],tot,pow2[N];
    ll n,t,ans,d;
    char s[N];
    
    inline int add(int x,int y){return x+y>mod?x+y-mod:x+y;}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    
    struct matrix{
        int c[210][210];
        void init(int d=0){
            for(int i=1;i<=k+1;i++)
                for(int j=1;j<=k+1;j++)
                    c[i][j]=0;
            for(int i=1;i<=k+1;i++) c[i][i]=d;
        }
        matrix operator * (matrix x){
            matrix res;
            res.init();
            for(int i=1;i<=k+1;i++)
                for(int j=1;j<=k+1;j++)
                    for(int l=1;l<=k+1;l++)
                        res.c[i][j]=add(res.c[i][j],1ll*c[i][l]*x.c[l][j]%mod);
            return res;
        }
    }Ans;
    
    matrix Fpow(matrix a,ll b){
        matrix res;
        res.init(1);
        while(b){
            if(b&1) res=res*a;
            a=a*a;
            b>>=1;
        }
        return res;
    }
    
    int main(){
        scanf("%lld%d",&n,&k);cnt=k;
        scanf("%s",s+1);
        m=strlen(s+1);
        for(int i=1;i<=m;i++)
            a[i]=s[i]-'a'+1;
        for(int i=m;i>=1;i--)
            if(!vis[a[i]]) q[--cnt]=a[i],vis[a[i]]=1;
        for(int i=1;i<=k;i++)
            if(!vis[i]) q[--cnt]=i;
        memset(pre,-1,sizeof(pre));
        t=min(n,1ll*k);
        for(int i=1;i<=m+t;i++){
            if(i>m) a[i]=q[tot],tot=(tot+1)%k;
            if(pre[a[i]]!=-1) f[i]=dec(add(f[i-1],f[i-1]),f[pre[a[i]]-1]);
            else f[i]=add(add(f[i-1],f[i-1]),1);
            pre[a[i]]=i;
        }
        if(n==t){
            printf("%d
    ",f[n+m]+1);
            return 0;
        }
        Ans.init();
        pow2[0]=1;
        for(int i=1;i<=k+1;i++) pow2[i]=pow2[i-1]*2%mod;
        Ans.c[1][k+1]=1;
        for(int i=2;i<=k+1;i++){
            for(int j=1;j<i-1;j++)
                Ans.c[i][j]=dec(mod,pow2[i-j-1]);
            Ans.c[i][i-1]=mod-1;
            Ans.c[i][k+1]=add(Ans.c[i][k+1],pow2[i-1]);
        }
        d=(n-1)/k,n-=d*k;
        Ans=Fpow(Ans,d);
        for(int i=0;i<=k;i++)
            ans=add(ans,1ll*f[m+i]*Ans.c[n+1][i+1]%mod);
        printf("%d
    ",ans+1);
        return 0;
    }
    
  • 相关阅读:
    1093 Count PAT's(25 分)
    1089 Insert or Merge(25 分)
    1088 Rational Arithmetic(20 分)
    1081 Rational Sum(20 分)
    1069 The Black Hole of Numbers(20 分)
    1059 Prime Factors(25 分)
    1050 String Subtraction (20)
    根据生日计算员工年龄
    动态获取当前日期和时间
    对计数结果进行4舍5入
  • 原文地址:https://www.cnblogs.com/jasony/p/13872579.html
Copyright © 2011-2022 走看看