UOJ#316. 【NOI2017】泳池

传送门
一道 D P DP DP 好题
q q q 为一个块合法的概率
套路一恰好为 k k k 的概率不好算,算小于等于 k k k 的减去小于等于 k − 1 k-1 k1
那么设 f i f_i fi 表示宽为 i i i 的合法的泳池面积都小于等于 k k k 的概率
g i g_i gi 表示宽为 i i i 的合法的泳池面积都小于等于 k k k 且最下面一行都合法的概率
那么考虑转移 f f f
套路二强制前面的满足一定的性质,后面接一段不满足的
首先 f i + = g i f_i+=g_i fi+=gi,然后枚举放一个不合法的块在最下面
那么贡献就是 ∑ j = 0 i − 1 f i − j − 1 g j ( 1 − q ) \sum_{j=0}^{i-1}f_{i-j-1}g_j(1-q) j=0i1fij1gj(1q)
考虑怎么得到 g g g,考虑到 g g g 表示的一定是下面都合法,上面是一个不合法的轮廓线的状态
那么枚举轮廓线的最下面的点
d p i , j dp_{i,j} dpi,j 表示宽为 i i i 最下面一行都合法,且向上第一个不合法的高度为 j + 1 j+1 j+1 的概率
仍然用套路二转移
首先 i × j ≤ k i\times j \le k i×jk
d p i , j = ( 1 − q ) q j ∑ l = 1 i − 1 ( ∑ x ≥ j d p l − 1 , x ) ( ∑ x > j d p i − l , x ) dp_{i,j}=(1-q)q^j\sum_{l=1}^{i-1}(\sum_{x\ge j}dp_{l-1,x})(\sum_{x>j}dp_{i-l,x}) dpi,j=(1q)qjl=1i1(xjdpl1,x)(x>jdpil,x)
显然有用的状态只有 Θ ( k l n k ) \Theta(klnk) Θ(klnk),然后前缀和优化做到 Θ ( k 2 l n k ) \Theta(k^2lnk) Θ(k2lnk)
直接这样子写就有 70 70 70
观察这个东西
g i + ∑ j = 0 i − 1 f i − j − 1 g j ( 1 − q ) g_i+\sum_{j=0}^{i-1}f_{i-j-1}g_j(1-q) gi+j=0i1fij1gj(1q)
就是个常系数齐次线性递推的形式
套用线性递推即可

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(1005);
const int mod(998244353);

inline void Inc(int &x, int y) {
	x = x + y >= mod ? x + y - mod : x + y;
}

inline int Pow(ll x, int y) {
	register ll ret = 1;
	for (; y; y >>= 1, x = x * x % mod)
		if (y & 1) ret = ret * x % mod;
	return ret;
}

int f[maxn], g[maxn], dp[maxn][maxn], n, pw[maxn], q;
int p[maxn], tmp[maxn << 1], a[maxn], b[maxn];

inline void Mul(int *x, int *y, int *z, int k) {
	register int i, j, inv, t = k << 1;
	memset(tmp, 0, sizeof(tmp));
	for (i = 0; i < k; ++i)
		for (j = 0; j < k; ++j) Inc(tmp[i + j], (ll)x[i] * y[j] % mod);
	for (i = t; i >= k; --i)
		if (tmp[i])
			for (inv = tmp[i], j = 0; j <= k; ++j) Inc(tmp[i - j], mod - (ll)p[k - j] * inv % mod);
	for (i = 0; i < k; ++i) z[i] = tmp[i];
}

inline int Solve(int k) {
	if (!k) return Pow(q, n);
	memset(dp, 0, sizeof(dp)), memset(f, 0, sizeof(f));
	register int i, j, l, ans = 0;
	for (i = 0; i <= 1001; ++i) dp[0][i] = 1;
	for (i = 1; i <= k; ++i)
		for (j = k / i; j; --j) {
			for (l = 1; l <= i; ++l) Inc(dp[i][j], (ll)dp[l - 1][j] * dp[i - l][j + 1] % mod);
			dp[i][j] = (ll)q * pw[j] % mod * dp[i][j] % mod;
			Inc(dp[i][j], dp[i][j + 1]);
		}
	for (i = 0; i <= k; ++i) g[i + 1] = (ll)q * dp[i][1] % mod, f[i] = dp[i][1];
	for (i = 1; i <= k; ++i)
		for (j = 1, l = min(i, k + 1); j <= l; ++j) Inc(f[i], (ll)f[i - j] * g[j] % mod);
	memset(p, 0, sizeof(p)), memset(a, 0, sizeof(a)), memset(b, 0, sizeof(b));
	for (p[++k] = 1, i = 0; i < k; ++i) p[i] = mod - g[k - i];
	a[0] = b[1] = 1;
	for (i = n; i; i >>= 1, Mul(b, b, b, k))
		if (i & 1) Mul(a, b, a, k);
	for (i = 0; i < k; ++i) Inc(ans, (ll)a[i] * f[i] % mod);
	return ans;
}

int k;

int main() {
	register int x, y, i;
	scanf("%d%d%d%d", &n, &k, &x, &y);
	pw[0] = 1, pw[1] = (ll)x * Pow(y, mod - 2) % mod, q = (mod + 1 - pw[1]) % mod;
	for (i = 2; i <= 1000; ++i) pw[i] = (ll)pw[i - 1] * pw[1] % mod;
	printf("%d\n", (Solve(k) - Solve(k - 1) + mod) % mod);
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值