CF645E Intellectual Inquiry
正好学校考试考到过一个加强版,写一写。
Solution
很明显的DP
如果 \(n=0\) ,设 \(f_i\) 表示到 \(i\) 位置之前不同的子序列数,可以得到两个转移方程:
-
第 \(i\) 位的字符之前没有出现过,方程就是 \(f_i=2\times f_{i-1}+1\)
意思是有 \(i-1\) 位时的方案,和 \({i-1}\) 位的每一种方案都加第 \(i\) 位上的字符,还有自己 \(1\) 种
-
第 \(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;
}