[ SDOI 2017 ] 硬币游戏
题目
思路
代码
#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;
}