zoukankan      html  css  js  c++  java
  • 【BZOJ4820】[SDOI2017]硬币游戏(高斯消元)

    【BZOJ4820】[SDOI2017]硬币游戏(高斯消元)

    题面

    BZOJ
    洛谷

    题解

    第一眼的感觉就是构(AC)自动机之后直接高斯消元算概率,这样子似乎就是(BZOJ1444)了。然而点数太多了,三方的消元没法做。
    考虑如何优化点数,首先我们的所有点可以分为两种,一种是终止节点,另外一种则不是。
    既然现在要某一个串出现,因此我们唯一需要考虑的是到达终止节点的情况。设(f_i)表示到达第(i)个串的终止位置,并且没有到达过其他终止节点的概率,也就是第(i)个串的答案。设(f_0)表示没有到达任何一个串终止位置的概率。
    那么显然的,要到达当前位置,我们一种可行的方法就是在没有匹配上任何一个串的串后面接上当前串,那么概率就是(f_0*frac{1}{2^m}),然而这个东西显然会比(f_i)要大,因为这个终止串再接上当前串可能包含了其他的串(j),而(f_0)表示的串没有匹配上任何一个串,意味着(j)的后缀是(i)的前缀。那么考虑所有其他串与当前串前后缀的匹配长度(k),我们可以列出方程:

    [f_0*frac{1}{2^m}=f_i+sum_j f_j*frac{1}{2^{m-k}} ]

    而然这样子是(n+1)元,(n)个方程,再利用(sum f_i=1)补足最后一个方程即可。
    好神仙啊。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define MAX 320
    const ull base=233;
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,m;char ch[MAX];
    ull h[MAX][MAX],pw[MAX];
    ull geths(int x,int l,int r){return h[x][r]-h[x][l-1]*pw[r-l+1];}
    double g[MAX][MAX],bin[MAX];
    void Guass()
    {
    	for(int i=0;i<=n;++i)
    	{
    		int p=i;
    		for(int j=i+1;j<=n;++j)if(fabs(g[j][i])>fabs(g[p][i]))p=j;
    		swap(g[p],g[i]);
    		double t=g[i][i];
    		for(int j=i;j<=n+1;++j)g[i][j]/=t;
    		for(int j=i+1;j<=n;++j)
    		{
    			double t=g[j][i];
    			for(int k=0;k<=n+1;++k)g[j][k]-=g[i][k]*t;
    		}
    	}
    	for(int i=n;i;--i)
    	{
    		g[i][n+1]/=g[i][i];
    		for(int j=i-1;j;--j)
    			g[j][n+1]-=g[i][n+1]*g[j][i];
    	}
    }
    int main()
    {
    	n=read();m=read();pw[0]=bin[0]=1;
    	for(int i=1;i<=m;++i)pw[i]=pw[i-1]*base,bin[i]=bin[i-1]/2;
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",ch+1);
    		for(int j=1;j<=m;++j)h[i][j]=h[i][j-1]*base+ch[j];
    	}
    	g[0][n+1]=1;
    	for(int i=1;i<=n;++i)
    	{
    		g[0][i]=1;g[i][0]=-bin[m];
    		for(int j=1;j<=n;++j)
    			for(int k=1;k<=m;++k)
    				if(geths(i,1,k)==geths(j,m-k+1,m))
    					g[i][j]+=bin[m-k];
    	}
    	Guass();
    	for(int i=1;i<=n;++i)printf("%.10lf
    ",g[i][n+1]);
    	return 0;
    }
    
  • 相关阅读:
    Java 如何删除 List 中的重复元素
    显示源代码及其子目录中的文件结构
    关于overflow:hidden的作用(溢出隐藏,清除浮动,解决外边塌陷等等)
    网页制作小技巧
    StringWriter/PrintWriter在Java输出异常信息中的作用
    [转载]利用@media screen实现网页布局的自适应,@media screen and
    OutputCache祥解
    string和stringBuilder区别
    sql执行顺序
    sql事务(Transaction)用法介绍及回滚实例
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10076926.html
Copyright © 2011-2022 走看看