BZOJ 2553 禁忌

首先我们要考虑给定一个串,如何将他划分,使得他有最多的禁忌串

我们只需要按里面出现的禁忌串们的出现的右端点排序然后贪心就可以啦

我们建出AC自动机,在AC自动机等价于走到一个包含禁忌串的节点就划分出一段

那么不妨设f(i,j)表示走了i步当前在AC自动机的j节点上

这样的DP方程跟BZOJ 1030是类似的

但是由于i可能会很大,但是j是很小的,又因为每一步的转移都是一样的

所以我们可以考虑矩阵乘法来优化DP

可是当我们用AC自动机构建了转移矩阵之后,我们会发现我们没办法算答案

我们只能算经过L步到达AC自动机某节点的概率

那么我们不妨新建节点表示答案,在每次出现禁忌串的时候在新建节点上贡献相应的期望

同时新建节点要将上次贡献得到的期望传递给下一次的转移

所以矩阵中n->n存在转移

值得一提的是:这个题目卡精度,我一开始写的double,结果WA了

去网上看了看题解之后默默的改成了long double,然后A了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;

const int maxn=102;
int n,L,k,cnt;
bool vis[maxn];
char s[maxn];
int t[maxn][26];
int fail[maxn];
bool end[maxn];
struct Matrix{
    long double a[maxn][maxn];
    Matrix(){memset(a,0,sizeof(a));}
}A,ans;
queue<int>Q;

void insert(){
    scanf("%s",s+1);
    int len=strlen(s+1),now=1;
    for(int i=1;i<=len;++i){
        int id=s[i]-‘a‘;
        if(!t[now][id])t[now][id]=++cnt;
        now=t[now][id];
    }end[now]=true;
}
void build_fail(){
    Q.push(1);fail[1]=0;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        end[u]|=end[fail[u]];
        for(int i=0;i<k;++i){
            int tmp=fail[u];
            while(tmp&&!t[tmp][i])tmp=fail[tmp];
            if(t[u][i]){
                fail[t[u][i]]=tmp?t[tmp][i]:1;
                Q.push(t[u][i]);
            }else t[u][i]=tmp?t[tmp][i]:1;
        }
    }A.a[n][n]=1;return;
}
void build_Matrix(){
    vis[1]=true;Q.push(1);
    long double tmp=1.0/k;
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=0;i<k;++i){
            if(!vis[t[u][i]]){
                vis[t[u][i]]=true;
                Q.push(t[u][i]);
            }
            if(end[t[u][i]]){
                A.a[u][n]+=tmp;
                A.a[u][1]+=tmp;
            }else A.a[u][t[u][i]]+=tmp;
        }
    }return;
}
Matrix operator *(const Matrix &A,const Matrix &B){
    Matrix C;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            for(int k=1;k<=n;++k){
                C.a[i][j]=C.a[i][j]+A.a[i][k]*B.a[k][j];
            }
        }
    }return C;
}
Matrix pow_mod(int p){
    Matrix tmp;
    for(int i=1;i<=n;++i)tmp.a[i][i]=1;
    while(p){
        if(p&1)tmp=tmp*A;
        A=A*A;p>>=1;
    }return tmp;
}

int main(){
    scanf("%d%d%d",&n,&L,&k);cnt=1;
    for(int i=1;i<=n;++i)insert();
    n=cnt+1;
    build_fail();build_Matrix();
    ans=pow_mod(L);
    printf("%.7lf\n",(double)(ans.a[1][n]));
    return 0;
}

  

时间: 2024-10-25 22:11:55

BZOJ 2553 禁忌的相关文章

【BZOJ 2553】[BeiJing2011]禁忌 AC自动机+期望概率dp

我一开始想的是倒着来,发现太屎,后来想到了一种神奇的方法——我们带着一个既有期望又有概率的矩阵,偶数(2*id)代表期望,奇数(2*id+1)代表概率,初始答案矩阵一列,1的位置为1(起点为0),工具矩阵上如果是直接转移那么就是由i到j概率期望都乘上1/alphabet,特别的,对于一个包含禁忌串的节点直接由其父节点指向0,而且在计算期望是多加上他的概率,最后统计答案时把答案矩阵上所有的期望加和即可,这个方法很完美的被卡精了....... #include <cstdio> #include

BZOJ 2553 BeiJing2011 禁忌 AC自动机+矩阵乘法

题目大意:给定n个模式串,定义一个字符串的伤害为所有子串的划分中最多包含的模式串数量,求长度为len的字符串的伤害期望值 小五prpr,恋恋prpr,大小姐prpr 首先建立AC自动机 令f[i][j]表示长度为i的字符串在AC自动机上的第j个节点的伤害期望值 如果要走到某个节点是危险节点或者fail指针指向危险节点,就ans++,然后回到根节点 这样构造出来的矩阵做快速幂= = 这么做都会把- - 不会别骂我- - 但是跑完发现找不到答案- - 因此我们需要稍微改造一下- - 新建一个节点 如

bzoj 2553: [BeiJing2011]禁忌

求所有长度为 len 的串s中,最多包含多少个不重叠的子串, 的期望. 开始觉得把s分成若干段这个操作让人无法下手, 但是其实一个简单的贪心就可以了, 因为如果所有子串不互相包涵的话, 一定是能取就取, 如果没有特殊限制的话, 可以把所有包含另一个子串的子串直接删掉或者是在建ac自动机的时候把end传下去(这样记录以当前点为后缀的串中是否含有一个子串)就可以了. 期望 = 这个状态出现的概率 * 贡献(恒为1) 所以就转化成了一个概率dp的问题. 此类题经典的转移状态: f[当前长度][当前在哪

AC自动机 + 矩阵优化 + 期望 --- [BJOI2011]禁忌

bzoj 2553 [BJOI2011]禁忌 题目描述: Magic Land上的人们总是提起那个传说:他们的祖先John在那个东方岛屿帮助Koishi与其姐姐Satori最终战平.而后,Koishi恢复了读心的能力-- 如今,在John已经成为传说的时代,再次造访那座岛屿的人们却发现Koishi遇到了新麻烦. 这次她遇到了Flandre Scarlet--她拥有可以使用禁忌魔法而不会受到伤害的能力. 为了说明什么是禁忌魔法及其伤害,引入以下概念: 1.字母集A上的每个非空字符串对应了一个魔法.

[Strings]一些字符串题目

Trie BZOJ 3689 异或之 大意: 给定n个数,求这n个数两两异或的值中的前k小 解: 将加法换成异或就变成了一个用堆合并多个有序表的经典问题,对于加法我们按大小排序就能得到有序表,而由于这里是异或,我们需要高效地维护有序表,即对于一个数ai快速求出与它异或第k小的数. 我们将所有数按二进制建成Trie,然后在Trie的结点上记录下子树中的结束结点个数,再在Trie树上走一遍就得到了答案 BZOJ 3439 Kpm的MC密码 大意: 给定n个字符串,对于每个字符串求以这个字符串为后缀的

BZOJ2553: [BeiJing2011]禁忌

2553: [BeiJing2011]禁忌 Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 203  Solved: 75[Submit][Status] Description Magic Land上的人们总是提起那个传说:他们的祖先John在那个东方岛屿帮助Koishi与其姐姐Satori最终战平.而后,Koishi恢复了读心的能力…… 如今,在John已经成为传说的时代,再次造访那座岛屿的人们却发现Kois

BZOJ 1013: [JSOI2008]球形空间产生器sphere

二次联通门 : BZOJ 1013: [JSOI2008]球形空间产生器sphere /* BZOJ 1013: [JSOI2008]球形空间产生器sphere 高斯消元 QAQ SB的我也能终于能秒题了啊 设球心的坐标为(x,y,z...) 那么就可以列n+1个方程,化化式子高斯消元即可 */ #include <cstdio> #include <iostream> #include <cstring> #define rg register #define Max

bzoj 3309 DZY Loves Math - 莫比乌斯反演 - 线性筛

对于正整数n,定义f(n)为n所含质因子的最大幂指数.例如f(1960)=f(2^3 * 5^1 * 7^2)=3, f(10007)=1, f(1)=0. 给定正整数a,b,求sigma(sigma(f(gcd(i,j)))) (i=1..a, j=1..b). Input 第一行一个数T,表示询问数. 接下来T行,每行两个数a,b,表示一个询问. Output 对于每一个询问,输出一行一个非负整数作为回答. Sample Input 4 7558588 9653114 6514903 445

【BZOJ】[HNOI2009]有趣的数列

[算法]Catalan数 [题解] 学了卡特兰数就会啦>_<! 因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一. 那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢. 将做偶数标为-1,做奇数标为+1,显然当偶数多于奇数时不合法,因为它压不住后面的奇数. 然后其实这种题目,打表就可知啦--QAQ 然后问题就是求1/(n+1)*C(2n,n)%p了,p不一定是素数. 参考bzoj礼物的解法. 看到网上清一色的素数筛+分解质因数解法,不解了好久,感觉写了假的礼物-- 后来觉得礼物的做法才比