[Sdoi2017]硬币游戏 [高斯消元 KMP]

[Sdoi2017]硬币游戏

题意:硬币序列,H T等概率出现,\(n \le 300\)个人猜了一个长为$ m \le 300$的字符串,出现即获胜游戏结束。求每个人获胜概率


考场用了[1444: Jsoi200]有趣的游戏的做法,40分

标解好神!

思想就是用N表示所有没有人获胜的状态,然后列方程

对于A,B两个串

例如:

A=TTH, B=HTT

那么N+TTH一定会到终止点,但不一定TTH加完后才停止

NTTH = A + BH + BTH

0.125N = A + 0.75B

获胜概率和为1

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

怎么解释呢?

就是把一些状态的概率用一些变量表示出来了呀

计算系数可以两两用kmp,求B再方程A中的系数:AB连起来求fail,然后得到后缀=前缀了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=305;
const double eps=1e-10;
inline int read() {
	char c=getchar(); int x=0, f=1;
	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
	return x*f;
}

int n, m;
char s[N][N];
double a[N][N], two[N];
namespace kmp {
	char s[N<<1];
	int fail[N];
	void build(char *s, int n) {
		fail[1] = 0;
		for(int i=2; i<=n; i++) {
			int now = fail[i-1];
			while(now && s[i] != s[now+1]) now = fail[now];
			fail[i] = s[i]==s[now+1] ? now+1 : 0;
		}
	}
	double cal(char *a, char *b) {
		int n = m<<1;
		for(int i=1; i<=m; i++) s[i] = a[i];
		for(int i=1; i<=m; i++) s[i+m] = b[i];
		build(s, n);
		//for(int i=1; i<=n; i++) printf("%d ", fail[i]); puts("");
		int now = fail[n]; double ans = 0;
		while(now) ans += two[m-now], now = fail[now];
		//printf("ans %lf\n", ans);
		return ans;
	}
}

void build() {
	two[0]=1;
	for(int i=1; i<=m; i++) two[i] = two[i-1] * 0.5;

	for(int i=1; i<=n; i++) a[n+1][i] = 1;
	a[n+1][n+2] = 1;
	for(int i=1; i<=n; i++) {
		for(int j=1; j<=n; j++) a[i][j] = kmp::cal(s[i], s[j]);
		a[i][n+1] = -two[m];
	}
}

void gauss(int n) {
	//for(int i=1; i<=n; i++) for(int j=1; j<=n+1; j++) printf("%lf%c", a[i][j], j==n+1 ? '\n' : ' ');
	for(int i=1; i<=n; i++) {
		int r=i;
		for(int k=i; k<=n; k++) if(abs(a[k][i]) > abs(a[r][i])) r=k;
		if(r != i) for(int j=i; j<=n+1; j++) swap(a[r][j], a[i][j]);

		for(int k=i+1; k<=n; k++) {
			double t = a[k][i] / a[i][i];
			for(int j=i; j<=n+1; j++) a[k][j] -= t * a[i][j];
		}
	}
	for(int i=n; i>=1; i--) {
		for(int j=n; j>i; j--) a[i][n+1] -= a[i][j] * a[j][n+1];
		a[i][n+1] /= a[i][i];
	}
}
int main() {
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);
	n=read(); m=read();
	for(int i=1; i<=n; i++) scanf("%s", s[i]+1);
	build();
	gauss(n+1);
	for(int i=1; i<=n; i++) printf("%.10lf\n", a[i][n+2]);
}

posted @ 2017-04-12 20:36  Candy?  阅读(1547)  评论(0编辑  收藏  举报