BZOJ 2553 [BeiJing2011]禁忌

AC自动机+矩阵快速幂

刚开始是想记f表示走到这的概率,g表示走到这的期望答案。写了一下,数据大概几百万就狂WA不止了,估计是计算量太大掉精度了?

然后发现g实际上没有什么用,用矩阵的一个位置来直接作前缀和就行了。

#include<cstdio>
#include<cstring>
#define S 28
#define L 20
#define N 150
using namespace std;
namespace runzhe2000
{
    typedef long double ld;
    char s[L];
    int n, len, totmen, alpha;
    struct matrix
    {
        ld a[N][N];
        matrix operator * (const matrix &that) const
        {
            matrix r; memset(r.a, 0, sizeof(r.a));
            for(int i = 1; i <= totmen; i++)
                for(int j = 1; j <= totmen; j++)
                    for(int k = 1; k <= totmen; k++)
                        r.a[i][j] += a[i][k] * that.a[k][j];
            return r;
        }
    }A;
    struct ACAM
    {
        ACAM *next[S], *fail;
        int ban;
    }mem[N], *tot, *null, *root, *q[N];
    ACAM *newACAM()
    {
        ACAM *p = ++tot;
        *p = *null; return p;
    }
    void init()
    {
        null = tot = mem;
        for(int i = 0; i < alpha; i++) null->next[i] = null;
        null->fail = null; null->ban = 0;
        root = newACAM();
    }
    void inser(char *s)
    {
        ACAM *p = root;
        for(int i = 0; s[i]; i++)
        {
            int w = s[i] - 'a';
            if(p->next[w] == null) p->next[w] = newACAM();
            p = p->next[w];
        }
        p->ban = 1;
    }
    void build()
    {
        root->fail = root; int head = 0, tail = 0;
        for(int i = 0; i < alpha; i++)
        {
            if(root->next[i] == null) root->next[i] = root;
            else root->next[i]->fail = root, q[tail++] = root->next[i];
        }
        for(; head < tail; head++)
        {
            ACAM *p = q[head];
            p->ban |= p->fail->ban;
            for(int i = 0; i < alpha; i++)
            {
                if(p->next[i] == null) p->next[i] = p->fail->next[i];
                else p->next[i]->fail = p->fail->next[i], q[tail++] = p->next[i];
            }
        }
    }
    void main()
    {
        scanf("%d%d%d",&n,&len,&alpha);init(); 
        for(int i = 1; i <= n; i++){scanf("%s",s);inser(s);} build();
        ACAM *p = mem+1; memset(A.a,0,sizeof(A.a)); ld beta = 1.0/(ld)alpha; 
        totmen = (tot-mem)<<1 | 1;
        for(; ; p++)
        {
            if(!p->ban)
                for(int i = 0; i < alpha; i++)
                {
                    if(p->next[i]->ban)
                    {
                        A.a[p-mem][root-mem] += beta;
                        A.a[p-mem][totmen] += beta;
                    }
                    else A.a[p-mem][p->next[i]-mem] += beta;
                }
            if(p == tot) break;
        }
        A.a[totmen][totmen] = 1;
        matrix r; memset(r.a,0,sizeof(r.a));
        for(int i = 1; i <= totmen; i++)    
            for(int j = 1; j <= totmen; j++)
                r.a[i][j] = i==j ? 1 : 0;
        for(; len; len >>= 1)
        {
            if(len & 1) r = r * A;
            A = A * A;
        }
        ld ans = r.a[1][totmen];
        printf("%.10lf\n",(double)ans);
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值