• bzoj2553: [BeiJing2011]禁忌


    传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2553

    思路:第一件事当然是建立AC自动机。。。

    现在我们建好了AC自动机,那么我们就在AC自动机上走,走到一个终止节点就算我们找到一个禁忌串,然后返回根节点重新匹配。

    和bzoj1030类似,考虑DP,设f[i][j]为现在长度为i,走到j号节点的期望。

    转移就是枚举下一个字符。下一个字符是终止节点就跳回root下次重新走,ans的期望就可以增加1/字符集大小。


    因为长度最大有10^9,显然直接DP会无论空间还是时间都会爆炸。。。

    所以用矩阵乘法+快速幂加速转移


    现在考虑怎么处理出初始的转移矩阵

    先算出a[i][j]表示i一步到j的概率

    用bfs就可以实现,如果j是i的儿子,那么a[i][j]+=1/字符集大小

    为了方便我们新建一个节点n=cnt(总结点数)+1

    每次转移root时也转移到它

    那么a[i][n]就是i走一步匹配到禁忌串的概率。

    要把所有步都累加出来,把a[n][n]赋为1就可以了

    因为这样下一次计算时b[root][n]=....+b[root][n]*a[n][n]+....

    就可以把上次的答案都累加起来了。

    自乘x次后,因为贡献永远是1,所以a[root][n]就表示root走x步遇到禁忌串的期望,也就是答案。


    最后吐槽一句:卡精度简直丧心病狂...不开long double就不让过...


    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    const int maxn=110;
    using namespace std;
    struct matrix{
    	long double a[maxn][maxn];
    	void clear(){for (int i=0;i<maxn;i++) for (int j=0;j<maxn;j++) a[i][j]=0.0;}
    }ans,f;
    int n,K,dsiz,num;char s[maxn];bool bo[maxn];
    inline matrix operator*(matrix a,matrix b){
    	matrix res;res.clear();
    	for (int i=0;i<=n;i++)
    		for (int j=0;j<=n;j++)
    			for (int k=0;k<=n;k++)
    				res.a[i][j]+=a.a[i][k]*b.a[k][j];
    	return res;
    }
    void qpow(){for (;K;K>>=1,f=f*f) if (K&1) ans=ans*f;}
    
    struct AC_DFA{
    	int tot,ch[maxn][26],fail[maxn],q[maxn],head,tail;bool end[maxn];
    	void insert(){
    		int len=strlen(s),p=0;
    		for (int i=0;i<len;p=ch[p][s[i]-'a'],i++) if (!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++tot;
    		end[p]=1;
    	}
    	void getfail(){
    		head=0,q[tail=1]=0,fail[0]=-1;
    		while (head!=tail){
    			int x=q[++head];
    			for (int i=0;i<dsiz;i++)
    				if (ch[x][i]) q[++tail]=ch[x][i],fail[ch[x][i]]=x==0?0:ch[fail[x]][i];
    				else ch[x][i]=x==0?0:ch[fail[x]][i];
    			end[x]|=end[fail[x]];
    		}
    	}
    	void build(){
    		head=0,q[tail=1]=0,bo[0]=1;
    		long double tmp=1.0/dsiz;
    		while (head!=tail){
    			int x=q[++head];
    			for (int i=0;i<dsiz;i++){
    				if (!bo[ch[x][i]]) bo[ch[x][i]]=1,q[++tail]=ch[x][i];
    				if (end[ch[x][i]]) f.a[x][n]+=tmp,f.a[x][0]+=tmp;
    				else f.a[x][ch[x][i]]+=tmp;
    			}
    		}
    	}
    }T;
    
    int main(){
    	scanf("%d%d%d",&num,&K,&dsiz);
    	for (int i=1;i<=num;i++) scanf("%s",s),T.insert();
    	n=T.tot+1,T.getfail(),T.build();
    	for (int i=0;i<=n;i++) ans.a[i][i]=1;
    	f.a[n][n]=1;qpow();
    	printf("%.7f
    ",(double)ans.a[0][n]);
    	return 0;
    }



  • 相关阅读:
    转:Visio之取消自动对齐与粘附
    转:Excel怎样修改图例名称
    一张图说明学习率和loss之间的关系
    转:loss不收敛或不下降问题处理经验
    N-UCLA骨架数据可视化
    转:IEEE论文投稿流程(格式说明,新手指南,模板)
    Ubuntu下无法安装sun-java6-jdk的解决办法
    Git 学习笔记一
    轮播特效小项目总结
    第9次作业--接口及接口回调
  • 原文地址:https://www.cnblogs.com/thythy/p/5493560.html
Copyright © 2020-2023  润新知