CF645E Intellectual Inquiry
阿新 • • 發佈:2020-10-25
正好學校考試考到過一個加強版,寫一寫。
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}\)
現在 \(n>0\) ,因為要最大數量,所以第二個方程中 \(f_{pre_{a_i}}\) 減的越少越好,那麼填的時候按 \(pre_{a_i}\) 從小到大依次填入最優
此時的複雜度是 \(O(n+m)\) ,完全足夠通過本題
但是,如果是 \(n\leq 10^{18}\)
這個時候,上面的方法就完全不行了
我們發現在填 \(n\) 的時候,每 \(k\) 位都算是一個迴圈節,而 \(k\leq 26\)
我們可以考慮用一些和 \(k\) 有關且能快速求出答案的方法——矩陣快速冪
將矩陣初始化的時候考慮第 \(i\)
時間複雜度: \(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; }