您的位置:首页 > 其它

BZOJ 2553: [BeiJing2011]禁忌(AC自动机+期望DP+矩阵快速幂)

2018-04-04 10:48 405 查看

传送吧

https://www.lydsy.com/JudgeOnline/problem.php?id=2553

思路

建出trie图。记f[i][j]为长度为i到节点j的期望。直接转移不行。

建出trie图的邻接矩阵,然后自乘len-1次转移。

具体就是如果当前点的儿子是禁忌点,就连向根(由儿子连长度多了1),否则连向儿子。这样就避免了重叠。而且有个显然的结论就是能走到禁忌点就走,肯定能取到最大值。

我们在每条边上放上概率,将值放在一个新建的点上,然后能走到禁忌点就连向它。不能放在根是显然的。转移就相当于i->k,k->j贡献i->j,就是在trie图上有方向的走(没方向考虑高消),然后统计答案。问的是从根走len步走到最终点的期望。

这道题告诉我们算期望可以先考虑概率,将值放在最后再乘。

long double是必要的,其他细节看代码。

代码

#include <bits/stdc++.h>
#define maxn 105

using namespace std;

int n, len, alp, cnt;
char x[maxn];

struct AC{
AC *son[26], *fail;
int ep, id;
void Clear(){
for(int i = 0; i < 26; i++)  son[i] = NULL;
ep = 0;
id = cnt;
}
}Node[maxn], *Root, *q[maxn];

struct Mat{
int r, c;
long double A[maxn][maxn];
void Clear(){
memset(A, 0, sizeof(A));
}
}f;

AC *NewTnode(){
Node[cnt].Clear();
return Node+cnt++;
}

void Insert(char *s){
AC *now = Root;
for(; *s != '\0'; s++){
int pos = (*s) - 'a';
if(!now->son[pos])  now->son[pos] = NewTnode();
now = now->son[pos];
}
now->ep = 1;
}

void Build(){
int hh = 0, tt = 0;
q[hh] = Root;
Root->fail = NULL;

while(hh <= tt){
AC *now = q[hh++];
for(int i = 0; i < alp; i++){
if(now->son[i]){
q[++tt] = now->son[i];
now->son[i]->fail = (now == Root) ? Root : now->fail->son[i];
now->son[i]->ep |= now->son[i]->fail->ep;
}
else  now->son[i] = (now == Root) ? Root : now->fail->son[i];
}
}
}

Mat operator * (Mat X, Mat Y){
Mat Z;
Z.r = X.r;  Z.c = Y.c;
Z.Clear();

for(int i = 0; i <= X.r; i++)
for(int j = 0; j <= Y.c; j++)
for(int k = 0; k <= X.c; k++)
Z.A[i][j] = (Z.A[i][j] + X.A[i][k] * Y.A[k][j]);
return Z;
}

Mat Pow(Mat X){
Mat Z = X;
len --;
while(len){
if(len & 1)  Z = Z * X;
X = X * X;
len >>= 1;
}
return Z;
}

int main(){

scanf("%d%d%d", &n, &len, &alp);

Root = NewTnode();

for(int i = 1; i <= n; i++){
scanf("%s", x);
Insert(x);
}

Build();

f.r = f.c = cnt;
f.Clear();

f.A[cnt][cnt] = 1.0;
long double possi = 1.0 / alp;

for(int i = 0; i < cnt; i++){
AC *now = Node+i;
for(int j = 0; j < alp; j++){
if(now->son[j]->ep){
f.A[i][0] += possi;
f.A[i][cnt] += possi;
continue;
}
f.A[i][now->son[j]->id] += possi;
}
}

f = Pow(f);

printf("%.7Lf\n", f.A[0][cnt]);

return 0;
}


What a good thing we lose

多么幸运 我们错失彼此

What a bad thing we knew

何其不幸 我们曾经相知
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: