[记录] 生成函数习题
[YZOJ7198] 暴政之王
初始有一个 \(1,2,3\dots,n\) 的排列,再随机一个排列,将初始排列根据这个置换 \(m\) 次。
现在给出最终结果,问有多少排列可能是随机出的那个排列。
\(n\le 10^5,m\le10^9\),模数为 \(10^9+7\)。
[YZOJ7249] Curiosity
一个 \(K\) 面骰子扔 \(N\) 次,记 \(i\) 的出现次数为 \(a_i\),求:
\[E[\ \prod_{i=1}^La_i^F\ ] \]\(0<N,K\le 10^9,0<F\le 1000,0<L\cdot F\le 50000,1\le L\le K\).
[YZOJ7283] Counting
有 \(m\) 个格子,从左往右标号为 \(0, 1, . . . , m − 1\)。
从 \(0\) 号格子出发,走 \(n\) 步,每步可以往左一格、原地不动、或者往右一格走一步,这个过程中不能走出格子
问有多少种方案可以回到 \(0\),答案对 \(998244353\) 取模。
\(1 ≤ n, m ≤ 10^7\).
首先把题意看作向前 \(0,1,2\) 格,最终走到第 \(n\) 格,避免往回走在组合意义上不好表示。
那么此时若没有不能走出格子的限制,\([x^n](1+x+x^2)^n\) 就是答案,然后 \((1+x+x^2)^n\) 其实是可以 \(O(n)\) 计算的。
令 \(H=1+x+x^2,F=H^n\),求导有 \(F'=n H^{n-1}H',F'H=nFH'\),然后可以根据这个等式一项项递推 \(F\)。
至于不能走出格子的限制可以用双线翻转解决,每次要求的就是走 \(n\) 次到第 \(n+\Delta\) 格的方案数,\(\Delta\) 与 \(n\) 同级。
查看代码
int main(){
read(n), read(m);
f[0] = inv[1] = 1;
lfor(i, 2, 2 * n) inv[i] = (LL) Mod(-mod / i) * inv[mod % i] % mod;
lfor(i, 0, 2 * n){
// (If[i] + If[i - 1] + If[i - 2]) = n * (f[i] + 2 * f[i - 1])
If[i] = (LL) n * Mod(F(i) + 2ll * F(i - 1) % mod - mod) % mod;
MOD(If[i] -= IF(i - 1)), MOD(If[i] -= IF(i - 2));
f[i + 1] = (LL) If[i] * inv[i + 1] % mod;
}
auto rev = [&](int &x, int &y, int b) -> void{ swap(x, y), x -= b, y += b; };
auto calc = [&](int x, int y) -> int{ return abs(x - y) <= n ? f[n + (x - y)] : 0; };
int Ans = calc(n, n), p, x, y;
p = 1, x = n, y = n;
while(x >= 0 && y >= 0){
if(p) rev(x, y, 1), MOD(Ans -= calc(x, y));
else rev(x, y, -m), MOD(Ans += calc(x, y) - mod);
p ^= 1;
}
p = 0, x = n, y = n;
while(x >= 0 && y >= 0){
if(p) rev(x, y, 1), MOD(Ans += calc(x, y) - mod);
else rev(x, y, -m), MOD(Ans -= calc(x, y));
p ^= 1;
}
cout << Ans << endl;
return 0;
}
[YZOJ7296] 师父
有 \(x,y\),初值都为 \(0\),然后进行 \(n\) 次操作。每次操作如下:
- 有 \(p\) 的概率,\(y\gets y+1,x\gets x+y\);
- 有 \(q\) 的概率,\(y\gets \min(y-1,0)\);
问最终 \(x\) 的期望。
\(n\le 10^7,p+q=1\)。
设 \(H_i\) 表示第 \(i\) 天 \(y\) 的期望值,故答案为 \(\sum_\limits{i=0}^{n-1}p\cdot(H_i+1)\).
考虑写出 \(H\) 的转移式
\(P_i\) 含义是,进行 \(i\) 次操作后,\(y=0\) 的概率,因为 \(y\) 是不会变成 \(-1\) 的,所以要加回来处理边界。
于是考虑 \(P_i\) 的转移
思路就是考虑上一次 \(y=0\) 是什么时候,然后乘上强制剩下一段都不能 \(y=0\) 的概率,也就是 \(S_i\) 的含义。
根据简单的组合数学知识,不难发现 \(S_{2i}={\rm Catalan}(i)\cdot (p\cdot q)^i\),所以可以写出 \(S(x)\) 的生成函数
然后让我们试图解出来 \(P(x)\) 的生成函数
然后只要展开就好了
查看代码
signed main(){
read(n), read(p), read(q), prep(2 * n);
P[0] = 1;
lfor(i, 1, n / 2) P[i * 2 - 1] = Mod(-mul(pwp[i], mul(pwq[i - 1], Cat(i - 1))));
lfor(i, 1, n) MOD(P[i] += P[i - 1] - mod);
H[0] = 0;
lfor(i, 1, n){
H[i] = Mod(H[i - 1] - mul(2, q));
MOD(H[i] += 1 + mul(q, P[i - 1]) - mod);
}
lfor(i, 0, n - 1) MOD(Ans += mul(p, H[i] + 1) - mod);
cout << Ans << endl;
return 0;
}