BZOJ 4820 [Sdoi2017]硬币游戏

题解:

设N为任何人都没获胜的状态

设仅有两个人

A=TTH B=HTT

另P(NTTH)表示出现NTTH串的概率,为0.125

另P(A)表示A获胜的概率

跟距前后缀关系列方程

则P(NTTH)=P(A)+0.25P(B)+0.5P(B)

三个人的话同理即可

还有P(A)+P(B)==1

n+1个变量,n+1个方程,高斯消元即可

出现的问题:

n和m不相等,不要用n的范围去预处理m

有的数组需要开两倍

自己理解的还不够深刻

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
using namespace std;
const int maxn=309;

int n,m;


char s[maxn][maxn];//people place

double fac[maxn];
double a[maxn][maxn];

char ss[maxn<<1];
int f[maxn<<1];
double Geta(int x,int y){
	double ret=0;
	for(int i=1;i<=m;++i)ss[i]=s[x][i];
	for(int i=1;i<=m;++i)ss[i+m]=s[y][i];
	
	f[1]=f[2]=1;
	for(int i=2;i<=m+m;++i){
		int j=f[i];
		while((j!=1)&&(ss[i]!=ss[j]))j=f[j];
		if(ss[i]==ss[j])f[i+1]=j+1;
		else f[i+1]=1;
	}
	int j=m+m+1;
	while(f[j]>m)j=f[j];
	while(f[j]!=1){
		ret+=fac[m-f[j]+1];
		j=f[j];
	}
	return ret;
}


double douabs(double x){
	if(x<0)return -x;
	else return x;
}	
void Guass(int n){
	for(int j=1;j<=n;++j){
		int maxline=j;
		for(int i=j+1;i<=n;++i){
			if(douabs(a[i][j])>douabs(a[maxline][j]))maxline=i;
		}
		if(maxline!=j){
			for(int k=j;k<=n+1;++k)swap(a[j][k],a[maxline][k]);
		}
		for(int i=j+1;i<=n;++i){
			double tmp=a[i][j]/a[j][j];
			for(int k=j;k<=n+1;++k){
				a[i][k]=a[i][k]-tmp*a[j][k];
			}
		}
	}

//	for(int i=1;i<=n;++i){
//		for(int j=1;j<=n;++j){
//			cout<<a[i][j]<<' ';
//		}
//		cout<<endl;
//	}
	for(int i=n;i>=1;--i){
		for(int j=i+1;j<=n;++j){
			a[i][n+1]=a[i][n+1]-a[i][j]*a[j][n+1];
		}
		a[i][n+1]/=a[i][i];
	}
}
	
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
	
	fac[0]=1;
	for(int i=1;i<=m;++i)fac[i]=fac[i-1]*0.5;
	
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			a[i][j]=Geta(i,j);
		}
		a[i][i]+=1;
		a[i][n+1]=-fac[m];
		a[i][n+2]=0;
	}
	for(int i=1;i<=n;++i)a[n+1][i]=1;
	a[n+1][n+1]=0;a[n+1][n+2]=1;
	
	Guass(n+1);
	
	for(int i=1;i<=n;++i)printf("%.10f\n",a[i][n+2]);
	
	return 0;
}

  

posted @ 2018-03-02 14:57  ws_zzy  阅读(207)  评论(0编辑  收藏  举报