题目链接

CF645E

题意

有一个长为\(n\)的由小写字母组成的字符串,需要用小写字母再填\(m\)位,使最后的字符串中本质不同的子串数量尽量多,答案对\(10^9+7\)取模。

本题数据:\(n,m\le 10^6\),事实上\(n\le10^6,m\le10^{18}\)也可以做

solution

先考虑\(m=0\)的情况,此时字符串确定,令\(f[i]\)表示前\(i\)位字符串中本质不同的子串数量,考虑到第\(i\)位时,新产生的子串是前\(i-1\)位所有本质不同的字符串最后接上第\(i\)位以及第\(i\)位自身。

于是\(f[i]=f[i-1]+f[i-1]+1\),但如果\(a[i](i出的字母)\),曾经在\(last[a[i]]\)处出现过,那么前\(last[a[i]]-1\)位字符串的子串与\(a[i]\)组合而成的字符串会重复,所以\(f[i]=f[i-1]+f[i-1]-f[last[a[i]-1]\)

当\(m>0\)时,为使答案最大,我们需要让靠前的\(last[a[i]]\)尽量小,所以填写时按\(last[i]\)从小到大依次填写一定最优,于是\(O(n+m)\)扫一遍,就可以通过CF645E此题。

发现填写序列时每\(k\)位一个循环,而\(k\le26\),所以可以矩阵快速幂优化到\(O(n+k^3log(m))\),能通过\(m\le10^{18}\)的数据

code

//O(n+m)算法
#include<bits/stdc++.h>
using namespace std;
const int M=1e9+7;
const int N=2e6+10;
int n,m,k,a[N],vis[N],q[N],cnt,f[N],last[N],tot=0;
char s[N];
inline int add(int x,int y,int mod=M){return (x+y>=mod)?x+y-mod:x+y;}
inline int dec(int x,int y,int mod=M){return (x-y<0)?x-y+mod:x-y;}
int main(){
scanf("%d%d",&m,&k);cnt=k;
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;++i) a[i]=s[i]-'a'+1;
for(int i=n;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(last,-1,sizeof(last));
for(int i=1;i<=n+m;++i){
if(i>n) a[i]=q[tot],tot=add(tot,1,k);
if(last[a[i]]!=-1) f[i]=dec(add(f[i-1],f[i-1]),f[last[a[i]]-1]);
else f[i]=add(add(f[i-1],f[i-1]),1);
last[a[i]]=i;
}
printf("%d\n",f[n+m]+1);
return 0;
}
//O(n+k^3log(m))算法
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=1e9+7;
const int N=4e6+10;
const int K=210;
int n,k,a[N],vis[N],q[N],cnt,f[N],last[N],tot=0,pw[N];
ll m;
char s[N];
inline int add(int x,int y,int mod=M){return (x+y>=mod)?x+y-mod:x+y;}
inline int dec(int x,int y,int mod=M){return (x-y<0)?x-y+mod:x-y;}
struct matrix{
int c[K][K];
void build(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 ret;ret.build();
for(int i=1;i<=k+1;++i)
for(int j=1;j<=k+1;++j)
for(int w=1;w<=k+1;++w)
ret.c[i][j]=add(ret.c[i][j],1ll*c[i][w]*x.c[w][j]%M);
return ret;
}
};
matrix operator ^(matrix a,ll k){
matrix ret;ret.build(1);
while(k){
if(k&1) ret=ret*a;
a=a*a;k>>=1;
}
return ret;
}
int main(){
scanf("%lld%d",&m,&k);cnt=k;
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;++i) a[i]=s[i]-'a'+1;
for(int i=n;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(last,-1,sizeof(last));
int t=min(m,k*1ll);
for(int i=1;i<=n+t;++i){
if(i>n) a[i]=q[tot],tot=add(tot,1,k);
if(last[a[i]]!=-1) f[i]=dec(add(f[i-1],f[i-1]),f[last[a[i]]-1]);
else f[i]=add(add(f[i-1],f[i-1]),1);
last[a[i]]=i;
}
if(m==t) printf("%d\n",f[n+m]+1);
else{
matrix A;A.build();
pw[0]=1;for(int i=1;i<=k+1;++i) pw[i]=2ll*pw[i-1]%M;
A.c[1][k+1]=1;
for(int i=2;i<=k+1;++i){
for(int j=1;j<i-1;++j)
A.c[i][j]=dec(M,pw[i-j-1]);
A.c[i][i-1]=M-1;
A.c[i][k+1]=add(A.c[i][k+1],pw[i-1]);
}
ll c=(m-1)/k,ans=0;m-=c*k;
A=A^c;
for(int i=0;i<=k;++i) ans=add(ans,1ll*f[n+i]*A.c[m+1][i+1]%M);
printf("%d\n",ans+1);
}
return 0;
}

最新文章

  1. Linux 查找进程运行位置
  2. Linux常用命令1
  3. Linux软件安装-RPM安装
  4. Android简单自定义圆形和水平ProgressBar
  5. java编码转换 unicode to utf-8
  6. XidianOJ 1172 Hiking
  7. ServiceManager 小结
  8. Easyui Layout Center 全屏方法扩展
  9. sequence 作为序列插入值不是第一个
  10. javascript一些常用函数
  11. 数据库范式(1NF 2NF 3NF BCNF)详解
  12. Ubuntu root登陆
  13. intel服务器cpu命名规则
  14. javascript 一些关于css操作的函数
  15. SQL Server性能优化——等待——SLEEP_BPROOL_FLUSH
  16. 前端笔记之服务器&amp;Ajax(中)MySQL基础操作&amp;PHP操作数据库&amp;Ajax
  17. JMeter 的调式工具
  18. PHP之PSR
  19. topcoder srm 430 div1
  20. (转)How Hash Algorithms Work

热门文章

  1. vue自定义指令 默认图片
  2. 正式班D20
  3. HashMap的put kv,是如何扩容的?
  4. iptables从入门到掌握
  5. 说说 C# 9 新特性的实际运用
  6. XML转换成TXT行数据的Java程序
  7. 嗖嗖移动大厅 源代码 Java初级小项目
  8. Java实现FTP跨服务器文件操作
  9. 使用JAVA API读取HDFS的文件数据出现乱码的解决方案
  10. Spider--补充--Re模块_2