zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    C#之正则表达式、异常处理和委托与事件
    C#之interface接口
    C#之类与对象
    C#第一次的Hello World
    第一次用博客
    用Visual Studio 2012+Xamarin搭建C#开发Andriod的环境
    cocos2dx 3.x for lua "异步加载"实现过程
    cocos2dx 3.x c++代码打包给lua调用过程(mac)
    cocos2dx 3.x lua 网络加载并且保存资源(unix、linux)
    path类和directory类对文件的路径或目录进行操作
  • 原文地址:https://www.cnblogs.com/psimonw/p/12031935.html
Copyright © 2011-2022 走看看