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

    点此看题面

    • (n)个人,每个人有一个长度为(m)的仅由HT组成的字符串。
    • 现往一个初始为空的字符串末尾加字符,每次等概率加入HT,对应字符串最早出现的人获胜。
    • 求每个人获胜的概率。
    • (n,mle300)

    一个没啥启发性的暴力

    可参见这道题:【BZOJ1444】[JSOI2009] 有趣的游戏

    就是一个很直接的建(AC)自动机+高斯消元的过程。

    当然由于本题的数据范围是(n,mle300),这种做法是过不了的,而且对于此题的正解也没啥启发性。(除了高斯消元?)

    神奇的正解

    我们设(S)表示不合法状态,设(T_i)表示第(i)个人获胜的状态。

    考虑在一个不合法状态之后加上(s_i),它必然能够结束,但不一定会等到(s_i)完全加完才结束。

    实际上,对于任意一个(T_j),只要满足(s_i)长度为(k)的前缀和(s_j)长度为(k)的后缀相同,(S+s_i[1...m])就有可能可以表示为(T_j+s_i[k+1...m])

    (S)(S+s_i[1...m])相当于要加上(m)个特定的字符,概率是(frac1{2^m});同理从(T_j)(T_j+s_i[k+1...m])相当于要加上(m-k)个特定字符,概率是(frac1{2^{m-k}})

    所以说,我们根据每个(s_i)都可以列出一个方程:

    [frac1{2^m}S=sum_{j=1}^nsum_{k=1}^m[s_i[1...k]=s_j[m-k+1...m]]frac1{2^{m-k}}T_j ]

    这样有(n+1)个元,(n)个方程,似乎还缺了什么。

    于是我们考虑到每局游戏总恰有一个人赢,也就是说实际上还有一个方程:

    [sum_{i=1}^nT_i=1 ]

    加上这个方程就可以高斯消元了。

    代码:(O(n^3))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300
    #define DB double
    using namespace std;
    int n,m;DB pp[N+5];char s[N+5][N+5];
    struct Hash
    {
    	#define ull unsigned long long
    	#define CU Con ull&
    	ull x,y;I Hash() {x=y=0;}I Hash(CU a) {x=y=a;}I Hash(CU a,CU b):x(a),y(b){}
    	I Hash operator + (Con Hash& o) Con {return Hash(x+o.x,y+o.y);}
    	I Hash operator - (Con Hash& o) Con {return Hash(x-o.x,y-o.y);}
    	I Hash operator * (Con Hash& o) Con {return Hash(x*o.x,y*o.y);}
    	I bool operator == (Con Hash& o) Con {return x==o.x&&y==o.y;}
    }seed(302627441,11743207),pre[N+5][N+5],suf[N+5][N+5],pw[N+5];
    DB a[N+5][N+5],v[N+5];I void Gauss(CI n)//高斯消元模板
    {
    	RI i,j,k;DB t;for(i=1;i<=n;++i)
    	{
    		for(j=i,k=i+1;k<=n;++k) fabs(a[k][i])>fabs(a[j][i])&&(j=k);for(k=i;k<=n;++k) swap(a[i][k],a[j][k]);
    		for(j=i+1;j<=n;++j) for(v[j]+=v[i]*(t=-a[j][i]/a[i][i]),k=i;k<=n;++k) a[j][k]+=a[i][k]*t;
    	}
    	for(i=n;i;--i) for(v[i]/=a[i][i],j=i-1;j;--j) v[j]-=a[j][i]*v[i];
    }
    int main()
    {
    	RI i,j;for(scanf("%d%d",&n,&m),pw[0]=pp[0]=i=1;i<=m;++i) pw[i]=pw[i-1]*seed,pp[i]=pp[i-1]/2;
    	for(i=1;i<=n;++i) scanf("%s",s[i]+1);
    	for(i=1;i<=n;++i) for(j=1;j<=m;++j) pre[i][j]=pre[i][j-1]*seed+s[i][j];//前缀哈希
    	for(i=1;i<=n;++i) for(j=m;j>=1;--j) suf[i][j]=suf[i][j+1]+pw[m-j]*s[i][j];//后缀哈希
    	RI k;for(i=1;i<=n;++i) for(j=1;j<=n;++j) for(k=1;k<=m;++k) pre[i][k]==suf[j][m-k+1]&&(a[i][j]+=pp[m-k]);//根据每个串列出方程
    	for(i=1;i<=n;++i) a[i][n+1]=-pp[m],a[n+1][i]=1;v[n+1]=1;//加上每个方程中S的项;列出第n+1个方程∑T=1
    	for(Gauss(n+1),i=1;i<=n;++i) printf("%.10lf
    ",v[i]);return 0;
    }
    
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    时间等待太重要!!!
    (引用 )自动化测试报告HTMLtestrunner
    (转载)selenium-webdriver(python)
    lr_convert_string_encoding()转码函数
    分步骤学习自动化测试
    (引用)web安全测试
    Monkey测试
    (学习网址)Python 自动化测试
    (引用) unittest测试驱动之执行测试(三)
    log4net日志组件
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4820.html
Copyright © 2011-2022 走看看