Loading

【题解】[USACO21DEC] HILO P

首先对于排列的每个数,只有部分数会对 \(S\) 产生贡献。

所以不难得到一个直接 DP 的方法,我们定义状态 \(f_{i,j,0/1}\) 表示 \(x\) 左边选了 \(i\) 个,\(x\) 右边选了 \(y\) 个,\(S\) 结尾是 "HI" 还是 "LO",产生了多少 "HILO"。但同时我们还要记录 \(g_{i,j,0/1}\) 表示对应的方案数。

直接转移是 \(\mathcal{O}(N^3)\),简化一下式子不难发现可以直接前缀和优化到 \(\mathcal{O}(N^2)\),但是因为常数过大一直过不去。

事实上有一个非常神奇的期望 DP 做法。由于总的方案数是 \(N!\),所以我们只用求出所有排列期望的 "HILO" 数。

期望 DP 一般是倒着推。因此,我们设计状态 \(f_{i,j,0/1}\) 表示 \(x\) 左边还剩有 \(i\) 个数,右边还剩 \(j\) 个数,字符串结尾是 "HI" 还是 "LO",期望的 "HILO" 数,\(0\) 表示 "LO"。

那么对于接下选择哪个数,概率都是 \(\frac{1}{i+j}\),所以 \(f_{i,j,0} = \frac{1}{i+j}(\sum\limits_{k<i} f_{k,j,0} + \sum\limits_{k<j}f_{i,k,1})\)

如果前面一个数是 \(1\)​ 而现在这个数是 \(0\)​,那么概率是 \(\frac{i}{i+j}\)​。所以 \(f_{i,j,1} = f_{i,j,0} + \frac{i}{i+j}\)​。前缀和优化即可。

#define N 5005
int n, m, inv[N], s1[N], s2[N];

int main() {
	read(n), read(m);
	rp(i, n)inv[i] = Pow(i, P - 2);
	rep(i, 0, m)rep(j, 0, n - m)if(i || j){
		int f = 1LL * (s1[i] + s2[j]) * inv[i + j] % P;
		int ff = (f + 1LL * i * inv[i + j]) % P;
		ad(s1[i], ff), ad(s2[j], f);
		if(i + j == n){
			rp(k, n)f = 1LL * f * k % P;
			cout << f << endl; return 0;
		}
	}
}
posted @ 2021-12-26 17:00  7KByte  阅读(182)  评论(0编辑  收藏  举报