BZOJ2553 Beijing2011禁忌(AC自动机+动态规划+矩阵快速幂+概率期望)
考虑对一个串如何分割能取得最大值。那么这是一个经典的线段覆盖问题,显然每次取右端点尽量靠前的串。于是可以把串放在AC自动机上跑,找到一个合法串后就记录并跳到根。
然后考虑dp。设f[i][j]表示前i位走到AC自动机上j节点的概率,枚举下个字符即可转移。同时记录此时期望伤害,找到合法串就统计入答案。
并且注意到每次转移是相同的。矩阵快速幂优化即可。
以及非常卡精度,需要全程long double。cout的保留小数位数误差是相当大的,必须用printf。并且转移到某个字符的概率即1/alphabet需要强制转换成long double,否则也会丢失精度。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 80 #define double long double int n,m,S,trie[N][26],fail[N],val[N],q[N],cnt=0; char c[20]; struct matrix { int n;double a[N][N]; matrix operator *(const matrix&b) const { matrix c;c.n=n;memset(c.a,0,sizeof(c.a)); for (int i=0;i<n;i++) for (int j=0;j<N;j++) for (int k=0;k<N;k++) c.a[i][j]+=a[i][k]*b.a[k][j]; return c; } }f,a; void ins(char *a) { int n=strlen(a+1),t=0; for (int i=1;i<=n;i++) { if (!trie[t][a[i]-'a']) trie[t][a[i]-'a']=++cnt; t=trie[t][a[i]-'a']; } val[t]=1; } void build() { int head=0,tail=0;for (int i=0;i<S;i++) if (trie[0][i]) q[++tail]=trie[0][i]; do { int x=q[++head]; for (int i=0;i<S;i++) if (trie[x][i]) q[++tail]=trie[x][i],fail[trie[x][i]]=trie[fail[x]][i],val[trie[x][i]]|=val[fail[trie[x][i]]]; else trie[x][i]=trie[fail[x]][i]; }while (head<tail); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2553.in","r",stdin); freopen("bzoj2553.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),S=read(); for (int i=1;i<=n;i++) scanf("%s",c+1),ins(c); build(); a.n=cnt+2; for (int i=0;i<=cnt;i++) for (int j=0;j<S;j++) if (val[trie[i][j]]) a.a[i][0]+=(double)1/S,a.a[i][cnt+1]+=(double)1/S; else a.a[i][trie[i][j]]+=(double)1/S; a.a[cnt+1][cnt+1]=1; f.n=1;f.a[0][0]=1; for (;m;a=a*a,m>>=1) if (m&1) f=f*a; printf("%.8Lf",f.a[0][cnt+1]); return 0; }