是[SDOI2017]硬币游戏的弱化版
但是也不完全一样
按照硬币游戏的题,$F(x)=sum a_i x^i$来刻画第i位结尾的概率生成函数
然后$G(x)$表示以i结尾,前面的随便选。$P(x)$表示前缀后缀的匹配
$F=G-F*(G+P)$然后G变成形式幂级数,分母乘过去,求导。
但是,这个题是**期望**而不是**概率**,
$F(x)=sum a_i ×i× x^i$把$(1/|Sigma|)$带入才是答案
i怎么出来呢?
求导!再集体乘上x!
也就是说,我们求的答案其实是:$(1/|Sigma|)F'(1/|Sigma|)$
对于刚才$F=G-F*(G+P)$求导之后,把$F'$移项过去,然后对着凑就可以了。
注意,虽然mod 1e4,但是n*n还是爆int的
代码:
#include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') #define int long long using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=1e5+5; const int mod=1e4; int s[N]; int m; int n; ll mi[N]; int nxt[N]; int qm(int x,int y){ int ret=1; while(y){ if(y&1) ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } int kmp(){ memset(nxt,0,sizeof nxt); nxt[1]=0; for(reg i=2,j=0;i<=m;++i){ while(j&&s[j+1]!=s[i]) j=nxt[j]; if(s[j+1]==s[i]) ++j; nxt[i]=j; } int ret=0; int st=m; while(nxt[st]){ ret=(ret+mi[nxt[st]])%mod; st=nxt[st]; } return ret; } int main(){ int t; rd(n);rd(t); mi[0]=1; for(reg i=1;i<=1e5;++i) mi[i]=mi[i-1]*n%mod; while(t--){ rd(m); for(reg i=1;i<=m;++i) rd(s[i]); int P=kmp(); int m1=qm(n,m);//,m2=qm(n,2*m-2); ll ans=(m1+P)%mod; printf("%04lld ",ans); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/2/23 18:09:25 */
求导得到i这一步值得积累。
求导不光能降次,还能把指数扔下来,也许还很有用!