zoukankan      html  css  js  c++  java
  • UOJ#316. 【NOI2017】泳池

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

    # 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
    ", (Solve(k) - Solve(k - 1) + mod) % mod);
    	return 0;
    }
    
  • 相关阅读:
    泛式之争
    测试的本质
    动态语言与静态语言
    对象之间的关系
    关于“重复”的一段交流
    装饰器与子类化
    类的设计质量
    抽象跟难
    Unity经典游戏编程之:球球大作战
    关于Unity 中对UGUI制作任务系统的编程
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/10200047.html
Copyright © 2011-2022 走看看