ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

KLAVIR

2022-08-02 12:31:27  阅读:145  来源: 互联网

标签:frac int wh fail 序列 KLAVIR 音符


link

题外话:蒟蒻 Feyn 在考场上成功地被这道题卡住了,两行的代码写了差不多两个小时一直憋不出来。另外有个小小的疑问,为啥其它同学最后的处理方法都和题解一毛一样呢,是我的思路过于非主流了还是什么……

约定:假设 \(A\) 是一个序列,\(a\) 是一个元素,那么 \(A+a\) 是在序列后拼上一个元素形成的新序列。\(t(A,B)\) 是指最长序列 \(C\) 的长度,满足其即是 \(A\) 的后缀又是 \(B\) 的前缀。

用 \(f_i\) 来代表弹奏完前 \(i\) 个音符的期望时间,那么我们可以把这个事件分成两个阶段。首先要弹完前 \(i\) 个音符就必须要完美地弹完前 \(i-1\) 个(这不废话),于是就必须对于最后一个音符分类讨论。显然有 \(\frac{1}{m}\) 的概率使得最后的那个音符刚好是我们想要的,此时只需要再弹一个音符即可;但更多的时候我们会选择到一个没用的音符,此时我们就回到了之前的某个状态。假设我们当前弹了的音符序列是 \(A\),当前弹错误音符是 \(a\),我们希望的音符序列是 \(B\),假如 \(len=t(A+a,B)\),那么我们就可以看成是已经弹了 \(len\) 个音符,接下来需要的期望时间理应是 \(f_i-f_{len}+1\),毕竟最后那个音符也是需要时间的。于是方程就可以列出来了:

\[f_{i}=f_{i-1}+\frac{1}{m}+\frac{1}{m}(f_i-f_{s_1})+\frac{1}{m}(f_i-f_{s_2})+\dots \]

其中 \(s_1\) 代表的是序列 \(t(A+1,B)\),以此类推。当然等号右边的式子不能包含能一步到位的那一项。移项整理可得:

\[f_i=(f_{i-1}+1)\times m-(f_{s_1}+f_{s_2}+\dots) \]

问题变成了如何求后半部分。如果暴力求很可能被卡成 \(O(N^2)\),考虑优化。

我们令上述式子的 \(f_{s_1}+f_{s_2}+\dots=g_{i-1}\),于是可以推出 \(g\) 的含义,即串的某个前缀后面拼上所有可能字符后对应位置的 \(f\) 值之和。既然我们希望的是极长的希望串前缀等于当前串后缀,那么显然 \(g_{i}\) 应该和 \(g_{fail_i}\) 有密切的关系。显然这段开始的那个式子大部分项应该是相同的,除了两个地方。首先根据定义,\(g_i\) 不能包括 \(f_{s_{A_{i+1}}}\),也就是不能包括使得下一个位置刚好符合条件的那一项,减掉即可;而由于 \(g_{fail_i}\) 也没有包括 \(f_{s_{A_{fail_i+1}}}\) 那一项(可能有点绕抱歉),所以要加上这一项。画一下就可以知道两个部分对应的位置,然后正常更新即可。代码异常简略,复杂度 \(O(N)\)。

#include<bits/stdc++.h>
//#define feyn
#define int long long
using namespace std;
const int N=1000010;
const int mod=1e9+7;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

int m,n,a[N],fail[N];
int f[N],g[N];

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	read(n);read(m);
	for(int i=1,j=0;i<=m;i++){
		read(a[i]);if(i==1)continue;
		while(j&&a[j+1]!=a[i])j=fail[j];
		if(a[j+1]==a[i])j++;fail[i]=j;
	}
	f[1]=n;
	for(int i=2;i<=m;i++){
		//核心代码
		g[i-1]=g[fail[i-1]]+f[fail[i-1]+1]-f[fail[i]];//从fail那里继承,然后加上缺失的部分丢掉不用的部分
		f[i]=n*(f[i-1]+1)-g[i-1];//和上面的式子一样更新即可
		//核心代码
		f[i]%=mod,g[i]%=mod;
	}
	for(int i=1;i<=m;i++)printf("%lld\n",(f[i]%mod+mod)%mod);
	
	return 0;
}

标签:frac,int,wh,fail,序列,KLAVIR,音符
来源: https://www.cnblogs.com/dai-se-can-tian/p/16543357.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有