[ SDOI 2017 ] 硬币游戏

题目

Luogu
LOJ
Acwing

思路

1.png
2.png
3.png
4.png
5.png
6.png
7.png
8.png
9.png

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#define double long double
using namespace std;
typedef unsigned long long ULL;
const int N = 310, P = 13331;
ULL p[N] = { 1 }, h[N][N];
int n, m;
string str[N];
double a[N][N], f[N] = { 1 };
// hash函数
ULL get(int x, int l, int r) { return h[x][r] - h[x][l - 1] * p[r - l + 1]; }
void Gauss() {
	int x = 1, y = 1;
	for (x = 1, y = 1; y <= n + 1; y++) {
		int t = x;
		for (int i = x; i <= n + 1; i++)
			if (fabs(a[i][y]) > fabs(a[t][y])) t = i;
		// 这里, 由于m可以取到300, 2的三百次方分之一......, eps反而会错
// 		if (fabs(a[t][y]) < eps) continue;
		for (int i = y; i <= n + 2; i++) swap(a[t][i], a[x][i]);
		for (int i = n + 2; i >= y; i--) a[x][i] /= a[x][y];
		for (int i = x + 1; i <= n + 1; i++)
		    for (int j = n + 2; j >= y; j--) 
				a[i][j] -= a[i][y] * a[x][j]; 
		x++;
	}
	for (int i = n; i > 0; i--)
		for (int j = i + 1; j <= n + 1; j++)
			a[i][n + 2] -= a[j][n + 2] * a[i][j];
}
int main() {
	cin >> n >> m;
	// 把字符串前面加一个空格, 下标就可以从 1 开始
	for (int i = 1; i <= n; i++) cin >> str[i], str[i] = ' ' + str[i];
	// p是hash用到的数组, f是2的n次方分之一
	for (int i = 1; i <= m; i++) p[i] = p[i - 1] * P, f[i] = 0.5 * f[i - 1];
	// 对于每个串, 预处理hash
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= m; j++)
			h[i][j] = h[i][j - 1] * P + str[i][j] - 'A';
	// 处理系数矩阵
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			for (int k = 1; k <= m; k++)
				if (get(i, 1, k) == get(j, m - k + 1, m))
					a[i][j] += f[m - k];
	// 处理 p0 那项
	for (int i = 1; i <= n; i++) a[i][n + 1] = -f[m];
	// 处理最后一行 所有p加起来答案是1
	for (int i = 1; i <= n; i++) a[n + 1][i] = 1;
	a[n + 1][n + 2] = 1;
	Gauss();
	for (int i = 1; i <= n; i++) cout << a[i][n + 2] << endl;
	return 0;
}
posted @ 2021-04-04 13:44  Protein_lzl  阅读(44)  评论(0编辑  收藏  举报