CF645E Intellectual Inquiry

洛谷传送门 CF传送门

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

Solution

很明显的DP

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

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

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

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

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

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

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


但是,如果是 \(n\leq 10^{18}\)

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

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

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

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

时间复杂度: \(O(m+k^3\log 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\n",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\n",ans+1);
    return 0;
}
posted @ 2020-10-25 11:29  jasony_sam  阅读(78)  评论(0编辑  收藏  举报