zoukankan      html  css  js  c++  java
  • [SDOI2017] 硬币游戏

    一、题目

    点此看题

    二、解法

    一定要先完全搞懂 歌唱王国 那个题再来做这道题,一模一样的思路。

    (F_i(x)) 表示 (A_i)(就表示那个硬币序列)在某个时刻出现的概率生成函数,(G(x)) 表示某个时刻还未结束的概率生成函数,现在就来找关系列方程吧!

    有一个方程是最难列的,但是思路是固定的。我们考虑对于 (A_i) 直接在 (G(x)) 的基础上强行加一个长度为 (m) 的字符串构成 (A_i),但是可能会有其他的 (A_j) 中途就已经被构成了,所以可能没有加到 (m) 个字符就停止了!

    但是这时候 (A_i) 的一个前缀一定等于 (A_j) 的一个后缀,我们枚举这个公共部分的长度,设 (a_{i,j,k}) 表示 (A_i[1,k]=A_j[m-k+1,m]),就可以结合 (F_j(x)) 列出这样的方程:

    [G(x)(frac{x}{2})^m=sum_{j=1}^nsum_{k=1}^m a_{i,j,k}F_j(x)(frac{x}{2})^{m-k} ]

    (x=1) 带入,因为我们只需要求 (F_i(1)),系数和也是相等的:

    [G(1)=sum_{j=1}^nsum_{k=1}^ma_{i,j,k}F_j(1)2^k ]

    这里已经有 (n+1) 个未知数和 (n) 个方程了,还有一个最简单的方程:

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

    直接高斯消元即可,时间复杂度 (O(n^3))震惊,(double) 竟然可以存 (2^{30})

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 305;
    #define db double
    #define eps 1e-9
    #define ull unsigned long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m;char s[M];ull h[M][M],pw[M];db a[M][M],b[M];
    ull Hash(int x,int l,int r)
    {
    	return h[x][r]-pw[r-l+1]*h[x][l-1];
    }
    db Abs(db x)
    {
    	return x>0?x:-x;
    }
    void guass(int n)
    {
    	for(int i=1;i<=n;i++)
    	{
    		int mx=i;
    		for(int j=i+1;j<=n;j++)
    			if(Abs(a[mx][i])<Abs(a[j][i]))
    				mx=j;
    		if(i!=mx) swap(a[i],a[mx]);
    		for(int j=1;j<=n;j++)
    		{
    			if(i==j) continue;
    			db t=a[j][i]/a[i][i];
    			for(int k=i;k<=n+1;k++)
    				a[j][k]-=a[i][k]*t;
    		}
    	}
    }
    signed main()
    {
    	n=read();m=read();pw[0]=b[0]=1;
    	for(int i=1;i<=m;i++)
    	{
    		pw[i]=pw[i-1]*2;
    		b[i]=b[i-1]*2;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%s",s+1);
    		for(int j=1;j<=m;j++)
    			h[i][j]=(s[j]=='H')+h[i][j-1]*2;
    	}
    	for(int i=1;i<=n;i++)//列第i个方程 
    	{
    		for(int j=1;j<=n;j++)
    			for(int k=1;k<=m;k++)
    				if(Hash(i,1,k)==Hash(j,m-k+1,m))
    					a[i][j]+=b[k];
    		a[i][n+1]=-1;
    	}
    	for(int i=1;i<=n;i++)
    		a[n+1][i]=1;
    	a[n+1][n+2]=1;
    	guass(n+1);
    	for(int i=1;i<=n;i++)
    		printf("%.10f
    ",a[i][n+2]/a[i][i]);
    }
    
  • 相关阅读:
    HDOJ 5294 Tricks Device 最短路(记录路径)+最小割
    国家人工智能(AI)的美好前景
    预防埃博拉病毒感染的试验疫苗投入人体试验
    MySQL同步复制搭建方法指南详细步骤
    正则表达式,用相反的方式过滤掉特殊字符
    Linux入门教程
    Linux:-bash: ***: command not found
    linux命令大全
    linux下打开、关闭tomcat,实时查看tomcat运行日志
    chmod u+x ./j2sdk-1_4_2_04-linux-i586.bin的含义
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14534577.html
Copyright © 2011-2022 走看看