loj2304. 「NOI2017」泳池

题意

略……

题解

以下默认(k, H = 1001)同阶。
先差分算两次(计算面积(leq k)的概率)。
考虑dp,设(dp_{i, j})表示当且考虑的矩形宽度为(j),从下往上第(i)行之下(i * j)个格子都是安全时,最大合法区域的面积(leq k)的概率。
状态转移一种是第(i + 1)层全部安全,一种是枚举第(i + 1)层的第一个不安全点,即

[dp_{i, j} = q ^ j * dp_{i + 1, j} + sum_{t = 1} ^ j (1 - q) q ^ {t - 1} dp_{i + 1, t - 1} dp_{i, j - t} ]

注意到在求出(i in [1, k], j in [0, frac {k}{i}])的所有dp值是(mathcal O(k ^ 2))的。
但是我们需要的是(dp_{0, n}),计算(dp_{0, i})需要很大代价,而(n)又很大,怎么办?
我们大力猜一波结论,猜(dp_{0, i})是一个(2 * k)阶线性齐次递推,大力BM一发,然后上Cayley-Hamilton即可。
复杂度(mathcal O(k ^ 2 log n))

#include <bits/stdc++.h>
using namespace std;
typedef vector <int> poly;
const int H = 2000, N = 2005, mod = 998244353;
int n, k, p, q, po[N], qo[N], dp[N][N];
void U (int &x, int y) {
	if ((x += y) >= mod) {
		x -= mod;
	}
}
int power (int x, int y) {
	int ret = 1;
	for ( ; y; y >>= 1, x = 1ll * x * x % mod) {
		if (y & 1) {
			ret = 1ll * ret * x % mod;
		}
	}
	return ret;
}
namespace polymain {
	void print (const poly &a) {
    	for (int i = 0; i < (int)a.size(); ++i) {
    		cerr << a[i] << " ";
		}
		cerr << endl;
	}
	poly operator + (const poly &a, const poly &b) {
		poly c(max(a.size(), b.size()));
		for (int i = 0; i < (int)a.size(); ++i) {
			c[i] = a[i];
		}
		for (int i = 0; i < (int)b.size(); ++i) {
			c[i] = (c[i] + b[i]) % mod;
		}
		return c;
	}
	poly operator - (const poly &a, const poly &b) {
		poly c(max(a.size(), b.size()));
		for (int i = 0; i < (int)a.size(); ++i) {
			c[i] = a[i];
		}
		for (int i = 0; i < (int)b.size(); ++i) {
			c[i] = (c[i] - b[i] + mod) % mod;
		}
		return c;
	}
	poly operator << (const poly &a, int b) {
		poly c(a.size() + b);
		for (int i = 0; i < (int)a.size(); ++i) {
			c[i + b] = a[i];
		}
		return c;
	}
	poly operator * (const poly &a, const poly &b) {
		poly c(a.size() + b.size() - 1);
		for (int i = 0; i < (int)a.size(); ++i) {
			for (int j = 0; j < (int)b.size(); ++j) {
				c[i + j] = (c[i + j] + 1ll * a[i] * b[j]) % mod;
			}
		}
		return c;
	}
	poly operator % (const poly &a, const poly &b) {
		poly c = a;
		for (int i = c.size() - 1; i >= (int)b.size() - 1; --i) {
			int v = c[i];
			for (int j = 1; j < (int)b.size(); ++j) {
				U(c[i - j], 1ll * v * b[j] % mod);
			}
		}
		c.resize(b.size() - 1, 0);
		return c;
	}
	poly initpoly (int c) {
		poly r(1);
		return r[0] = c, r;
	}
	poly Berlekamp_Massey (int S[], int n) {
		poly Ci = initpoly(1), Cj = initpoly(1); int b = 1;
		for (int i = 0, j = -1; i < n; ++i) {
			int d = 0;
			for (int k = 0; k < (int)Ci.size(); ++k) {
				d = (1ll * Ci[k] * S[i - k] + d) % mod;
			}
			if (d) {
				poly tmp = Ci;
				Ci = Ci - ((Cj * initpoly(1ll * d * power(b, mod - 2) % mod) << (i - j)));
				if ((int)Cj.size() - j > (int)tmp.size() - i) {
					Cj = tmp, b = d, j = i;
				}
			}
		}
		return Ci;
	}
	int Cayley_Hamilton (int *a, poly c, int k, int n) {
		poly r = initpoly(1) << 1, s = initpoly(1);
		for ( ; n; n >>= 1, r = r * r % c) {
			if (n & 1) {
				s = s * r % c;
			}
		}
		int ret = 0;
		for (int i = 0; i < k - 1; ++i) {
			U(ret, 1ll * a[i] * s[i] % mod);
		}
		return ret;
	}
}
int calc (int *a, int k, int n) {
	poly c = polymain :: Berlekamp_Massey(a, k + 1);
	for (int i = 0; i < (int)c.size(); ++i) {
		c[i] = (mod - c[i]) % mod;
	}
	return polymain :: Cayley_Hamilton(a, c, c.size(), n);
}
int solve (int k) {
	int h = min(n, H);
	memset(dp, 0, sizeof dp);
	for (int i = 0; i <= k + 1; ++i) {
		dp[i][0] = 1;
	}
	for (int i = k; ~i; --i) {
		for (int j = 1; j <= h && j * i <= k; ++j) {
			U(dp[i][j], 1ll * dp[i + 1][j] * po[j] % mod);
			for (int l = 1; l <= j; ++l) {
				U(dp[i][j], 1ll * dp[i + 1][l - 1] * po[l - 1] % mod * q % mod * dp[i][j - l] % mod);
			}
		}
	}
	if (n <= h) {
		return dp[0][n];
	}
	return calc(dp[0], h, n);
}
int main () {
	cin >> n >> k >> p >> q;
	p = 1ll * p * power(q, mod - 2) % mod, q = (1 + mod - p) % mod;
	po[0] = qo[0] = 1;
	for (int i = 1; i < N; ++i) {
		po[i] = 1ll * po[i - 1] * p % mod;
		qo[i] = 1ll * qo[i - 1] * q % mod;
	}
	cout << (solve(k) - solve(k - 1) + mod) % mod << endl;
	return 0;
}
原文地址:https://www.cnblogs.com/psimonw/p/12031935.html