• BZOJ2553 [BeiJing2011]禁忌 AC自动机 矩阵


    原文链接http://www.cnblogs.com/zhouzhendong/p/8196279.html


    题目传送门 - BZOJ2553


    题意概括

      引用一下lych大佬的:

      在字母只有前alphabet时,给定N个串,求长度为len的串包含这些N个串的个数最大值的期望值。


    题解

      我们首先发现总共的字符个数才就75个,那么闭着眼睛先建一个AC自动机,Trie图建好。反正代码不长。

      然后我们发现长度很大,显然就是要往矩阵快速幂那里考虑。

      我们可以用矩阵来快速计算到达AC自动机的每一个位置的概率。

      然后我们考虑原问。

      对于一个串,把它划分成包含最多的禁忌串数的方案怎么做?

      我们可以贪心的来,从头开始沿着字符串在AC自动机上面走,每一次到一个结束点就划分。这样一定是最优的。

      然后回到矩阵中。

      假如说我们考虑DP的话,那么每到一个结束点,都要统计一下当前概率对答案的贡献,就是概率*1

      那么我们可以把它记入答案中。

      同理,我们可以在矩阵中多开一列,用来维护答案。


    代码

    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    typedef long double LD;
    const int S=80;
    struct Trie{
    	int fail,e,Next[26];
    	void clear(){
    		fail=e=0;
    		memset(Next,0,sizeof Next);
    	}
    }tree[S];
    int n,len,alpha,cnt;
    void build(char ch[]){
    	int rt=1,t,len=strlen(ch);
    	for (int i=0;i<len;i++){
    		t=ch[i]-'a';
    		if (!tree[rt].Next[t]){
    			tree[++cnt].clear();
    			tree[rt].Next[t]=cnt;
    		}
    		rt=tree[rt].Next[t];
    	}
    	tree[rt].e=1;
    }
    void build_AC(){
    	int q[S],head=0,tail=0;
    	int rt,son,k;
    	q[++tail]=1,tree[0].fail=1;
    	while (head<tail){
    		int rt=q[++head];
    		for (int i=0;i<alpha;i++){
    			son=tree[rt].Next[i];
    			if (!son){
    				tree[rt].Next[i]=tree[tree[rt].fail].Next[i];
    				continue;
    			}
    			k=tree[rt].fail;
    			while (!tree[k].Next[i])
    				k=tree[k].fail;
    			tree[son].fail=tree[k].Next[i];
    			tree[son].e|=tree[tree[k].Next[i]].e;
    			q[++tail]=son;
    		}
    	}
    }
    int m;
    struct Mat{
    	LD v[S][S];
    	Mat (){}
    	Mat (int x){set(x);}
    	void print(){
    		for (int i=1;i<=m;i++,puts(""))
    			for (int j=1;j<=m;j++)
    				printf("%5.3Lf ",v[i][j]);
    		puts("");
    	}
    	void set(int x){
    		memset(v,0,sizeof v);
    		if (x==1)
    			for (int i=1;i<=m;i++)
    				v[i][i]=1;
    	}
    	Mat operator * (Mat b){
    		Mat ans(0);
    		for (int i=1;i<=m;i++)
    			for (int j=1;j<=m;j++)
    				for (int k=1;k<=m;k++)
    					ans.v[i][j]+=v[i][k]*b.v[k][j];
    		return ans;
    	}
    	Mat operator ^ (int y){
    		Mat ans(1),x=*this;
    		while (y){
    			if (y&1)
    				ans=ans*x;
    			x=x*x;
    			y>>=1;
    		}
    		return ans;
    	}
    }M(0);
    char s[S];
    int main(){
    	scanf("%d%d%d",&n,&len,&alpha);
    	cnt=1;
    	tree[0].clear();
    	tree[1].clear();
    	for (int i=0;i<alpha;i++)
    		tree[0].Next[i]=1;
    	for (int i=1;i<=n;i++){
    		scanf("%s",s);
    		build(s);
    	}
    	build_AC();
    	m=cnt+1;
    	LD addv=1.0/(1.0*alpha);
    	for (int i=1;i<=cnt;i++)
    		for (int j=0;j<alpha;j++)
    			if (tree[tree[i].Next[j]].e)
    				M.v[i][1]+=addv,M.v[i][m]+=addv;
    			else
    				M.v[i][tree[i].Next[j]]+=addv;
    	M.v[m][m]=1;
    	Mat Mans=M^len;
    	printf("%.10lf",(double)Mans.v[1][m]);
    	return 0;
    }
    

      

  • 相关阅读:
    First duplicate value
    SQL学习笔记day1
    Find closest value in BST
    BST construction
    Closest sum_pair
    滑动窗口 sliding window
    设计模式(3)观察者模式
    设计模式(1)装饰模式总结
    深刻探讨public class=new class();
    与时间赛跑,我的2012
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ2553.html
Copyright © 2020-2023  润新知