• luogu P3706 [SDOI2017]硬币游戏


    luogu

    惯性思维想到建出AC自动机,然后高斯消元,再只考虑有用的项即可

    考虑概率生成函数,设(F_i(x))(x)获胜的概率生成函数,即第(j)项为序列长度为(j)(i)获胜的概率,设(G(x))为到某个时刻还没人获胜的概率生成函数,可以列出两个柿子

    (G(x)+sum_{i=1}^{n}F_i(x)=xG(x)+1)

    (iin[1,n],(frac{x}{2})^mG(x)=sum_{j=1}^{m}sum_{k=1}^{n}[s_k[m-j+1,m]=s[1,j]](frac{x}{2})^{m-j}F_k(x))

    第一个的意思是在一个未结束的序列后接个字符可能有人获胜可能每人获胜

    第二个的意思是在一个没结束的序列后直接接上某个人的串,按道理他会获胜,但是可能接到一半就已经有人获胜了,假设往后接了(j)个字符后(k)获胜,那么这种情况出现代表(k)的串的长度为(j)的后缀和(i)的串的长度为(j)的前缀缀相同

    我们要求的实际上是(F_i(1)),所以带入(x=1)后可得

    (sum_{i=1}^{n}F_i(1)=1)

    (iin[1,n],(frac{x}{2})^mG(1)=sum_{j=1}^{m}sum_{k=1}^{n}[s_k[m-j+1,m]=s[1,j]](frac{x}{2})^{m-j}F_k(1))

    就有(n+1)个变量和(n+1)个方程了,高斯消元即可

    #include<bits/stdc++.h>
    #define LL long long
    #define uLL unsigned long long
    #define db double
    
    using namespace std;
    const int N=300+10;
    const db eps=1e-8;
    int rd()
    {
        int x=0,w=1;char ch=0;
        while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
        return x*w;
    }
    db a[N][N],p2[N];
    char cc[N];
    uLL ha[N][N],pw[N],bs=233;
    uLL gha(int o,int l,int r){return ha[o][r]-ha[o][l-1]*pw[r-l+1];}
    int n,m;
    
    int main()
    {
    	n=rd(),m=rd();
    	pw[0]=1;
    	for(int i=1;i<=m;++i) pw[i]=pw[i-1]*bs;
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%s",cc+1);
    		for(int j=1;j<=m;++j) ha[i][j]=ha[i][j-1]*bs+cc[j];
    	}
    	p2[0]=1;
    	for(int i=1;i<=m;++i) p2[i]=p2[i-1]*0.5;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			for(int k=1;k<=n;++k)
    				if(gha(k,m-j+1,m)==gha(i,1,j)) a[i][k]+=p2[m-j];
    	for(int i=1;i<=n;++i)
    		for(int k=1;k<=n;++k)
    			a[i][k]-=a[i+1][k];//差分少掉一个变量&方程
    	for(int k=0;k<=n;++k) a[n][k]=1;
    	for(int i=1;i<=n;++i)
    	{
    		if(fabs(a[i][i])<eps)
    		{
    			for(int j=i+1;j<=n;++j)
    				if(fabs(a[j][i])>eps)
    				{
    					for(int k=0;k<=n;++k) swap(a[i][k],a[j][k]);
    					break;
    				}
    		}
    		if(fabs(a[i][i])<eps) break;
    		for(int j=1;j<=n;++j)
    		{
    			if(i==j) continue;
    			db gg=a[j][i]/a[i][i];
    			for(int k=0;k<=n;++k) a[j][k]-=a[i][k]*gg;
    		}
    	}
    	for(int i=1;i<=n;++i) printf("%.8lf
    ",a[i][0]/a[i][i]);
    	return 0;
    }
    
  • 相关阅读:
    逻辑思维题:称金币
    Windows7中Emacs 24 shell使用Gitbash
    Android中Touch事件分析--解决HorizontalScrollView滑动和按钮事件触发问题
    hdu 3732 Ahui Writes Word
    lucene基本原理
    elasticsearch分析系列
    主流的自动化运维工具
    IDEA在当前类中查找方法快捷键--转
    迷你MVVM框架 avalonjs 0.81发布
    最火的前端开源项目
  • 原文地址:https://www.cnblogs.com/smyjr/p/12854573.html
Copyright © 2020-2023  润新知