3 条题解
-
5
这里给出 metaphysis 补充后的题解,原链接。
前置知识
(1)生成函数(解决组合或排列问题的一种有力工具):
(2)拉格朗日反演(根据复合逆得到函数的某项系数):
正文
浏览一遍题目,可知等概率生成一棵符合题目约束的树,其 的期望值等于所有树的权值 之和除以不同构树的总数 。由于 , 均为整数,可知 必定是整数,而 显然是整数,当 较小时,可以暴力生成所有可能的树,分别统计权值和得到答案。当 较大时,需要寻找高效的方法(附注:测试点 是给暴力生成树的解题者设置,测试点 是给暴力生成树并加以适当剪枝的解题者设置,测试点 和 是为潜在的 动态规划设置;测试点 和 留给愿意写多项式算法的解题者,时间 ,要注意常数)。
首先给出结论:
$$\large \mathbb{E}(v(\mathcal T)) = \frac{[x^{n-1}]\left(\left(1+x^{k_1}+x^{k_2}\right)^{n-1}\left(ax^{k_1}+bx^{k_2}\right)\right)}{\dfrac{1}{n}[x^{n-1}]\left(1+x^{k_1}+x^{k_2}\right)^n} $$先确定分母。令符合题意要求的由 个点组成的不同构的树的数量所构成序列的普通生成函数为 ,由题意,只有 个结点的任意 的数量为 ,因此 的一次幂 的系数为 ,根结点有 棵子树(或者 棵子树),这 棵子树(或者 棵子树)总的结点数为 ,且子树也是 ,结合判定同构的规则, 可以得到:
$$\mathbf F(x)=x\left(1+\mathbf F^{k_1}(x)+\mathbf F^{k_2}(x)\right) $$观察可知 的零次项系数为 ,一次项系数为 ,且:
$$x=\dfrac{x\left(1+\mathbf F^{k_1}(x)+\mathbf F^{k_2}(x)\right)}{1+\mathbf F^{k_1}(x)+\mathbf F^{k_2}(x)}=\dfrac{\mathbf F(x)}{1+\mathbf F^{k_1}(x)+\mathbf F^{k_2}(x)} $$因此其复合逆为:
$$\mathbf{F}^{\langle-1\rangle}(x)=\dfrac{x}{1+x^{k_1}+x^{k_2}} $$根据拉格朗日反演,有:
$$[x^n]\mathbf F(x)=\dfrac{1}{n}[x^{n-1}]\left(1+x^{k_1}+x^{k_2}\right)^n $$正是分母。
现在要考虑求所有树的权值和,一个常见的思路是使用多元生成函数,然后求导。
令
$$\mathbf F(x,\ z)=\displaystyle \sum f_{n,m}x^nz^m $$其中 表示 个结点的 权值和为 的方案数,由于根结点要么有 棵子树,要么有 棵子树,要么没有子树。如果有 棵子树,加上根结点后,又构成了一个 叉结点,每种方案增加了 分。如果有 棵子树,加上根结点后,又构成了一个 叉结点,每种方案增加了 分。如果树中只包含根结点,则由于 个结点的 权值和为 的方案数为 ,故有:
$$\mathbf F(x,\ z)=x\left( 1 + z^{a}\mathbf F^{k_1}(x,z) + z^b\mathbf F^{k_2}(x,z) \right) $$类似地,容易发现 这一维的复合逆是:
$$\mathbf F_x^{\langle -1\rangle}(x,\ z)=\dfrac{x}{1+z^{a}x^{k_1}+z^bx^{k_2}} $$显然多元生成函数也可以进行拉格朗日反演:
$$[x^n]\mathbf F(x,\ z)=\frac{1}{n}[x^{n-1}]\left( 1+z^ax^{k_1}+z^{b}x^{k_2} \right)^n $$令:
$$\zeta (x,\ z)=\left(1+z^ax^{k_1}+z^bx^{k_2}\right)^n $$现在要求权值和,对 这一维求导:
$$\frac{\partial\zeta(x,\ z)}{\partial z}=n\left( 1+z^ax^{k_1}+z^bx^{k_2} \right)^{n-1}\left(az^{a-1}x^{k_1}+ bz^{b-1}x^{k_2} \right) $$显然权值和就是:
$$[x^n]\frac{\partial \mathbf F(x,\ z)}{\partial z}\biggr\vert_{z=1}=\frac{1}{n}[x^{n-1}]\frac{\partial\zeta(x,\ z)}{\partial z}\biggr\vert_{z=1}=[x^{n-1}]\left ( \left( 1 +x^{k_1}+x^{k_2} \right)^{n-1}\left( ax^{k_1}+bx^{k_2} \right) \right) $$可以 枚举 选了几次,用多项式系数计算答案即可。由于需要求同余方程的解,可以将求逆元与解题代码有机结合。
出题人给出的标程:
#include <bits/stdc++.h> using namespace std; const int N = 1e7 + 5, mod = 998244353; inline int inc(int a, int b) { return (a += b - mod) + (a >> 31 & mod); } inline int dec(int a, int b) { return (a -= b) + (a >> 31 & mod); } inline int mul(int a, int b) { return 1ll * a * b % mod; } inline void Inc(int &a, int b) { return void(a = inc(a, b)); } inline void Dec(int &a, int b) { return void(a = dec(a, b)); } inline void Mul(int &a, int b) { return void(a = mul(a, b)); } inline int Pow(int a, int b) { int c = 1; for (; b; Mul(a, a), b >>= 1) if (1 & b) Mul(c, a); return c; } inline int Inv(int x) { return Pow(x, mod - 2); } int fac[N], inv[N], n, k1, k2, a, b, tot, Sum; inline int Polycoef(int n, int m, int k) { return mul(fac[n + m + k], mul(mul(inv[n], inv[m]), inv[k])); } int main(void) { scanf("%d%d%d%d%d", &k1, &k2, &n, &a, &b); fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i); inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = mul(mod - mod / i, inv[mod % i]); inv[0] = 1; for (int i = 1; i <= n; i++) Mul(inv[i], inv[i - 1]); for (int i = 0; i <= n; i++) if (i * k1 <= n - 1) { int res = n - 1 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n) continue; Inc(tot, Polycoef(i, j, n - i - j)); } for (int i = 0; i <= n - 1; i++) if (i * k1 <= n - 1 - k1) { int res = n - 1 - k1 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n - 1) continue; Inc(Sum, mul(Polycoef(i, j, n - 1 - i - j), a)); } for (int i = 0; i <= n - 1; i++) if (i * k1 <= n - 1 - k2) { int res = n - 1 - k2 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n - 1) continue; Inc(Sum, mul(Polycoef(i, j, n - 1 - i - j), b)); } Mul(tot, Inv(n)); return printf("%d\n", mul(Sum, Inv(tot))) * 0; }
-
2
#include <bits/stdc++.h> using namespace std; const int N = 1e7 + 5, mod = 998244353; inline int inc(int a, int b) { return (a += b - mod) + (a >> 31 & mod); } inline int dec(int a, int b) { return (a -= b) + (a >> 31 & mod); } inline int mul(int a, int b) { return 1ll * a * b % mod; } inline void Inc(int &a, int b) { return void(a = inc(a, b)); } inline void Dec(int &a, int b) { return void(a = dec(a, b)); } inline void Mul(int &a, int b) { return void(a = mul(a, b)); } inline int Pow(int a, int b) { int c = 1; for (; b; Mul(a, a), b >>= 1) if (1 & b) Mul(c, a); return c; } inline int Inv(int x) { return Pow(x, mod - 2); } int fac[N], inv[N], n, k1, k2, a, b, tot, Sum; inline int Polycoef(int n, int m, int k) { return mul(fac[n + m + k], mul(mul(inv[n], inv[m]), inv[k])); } int main(void) { scanf("%d%d%d%d%d", &k1, &k2, &n, &a, &b); fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i); inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = mul(mod - mod / i, inv[mod % i]); inv[0] = 1; for (int i = 1; i <= n; i++) Mul(inv[i], inv[i - 1]); for (int i = 0; i <= n; i++) if (i * k1 <= n - 1) { int res = n - 1 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n) continue; Inc(tot, Polycoef(i, j, n - i - j)); } for (int i = 0; i <= n - 1; i++) if (i * k1 <= n - 1 - k1) { int res = n - 1 - k1 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n - 1) continue; Inc(Sum, mul(Polycoef(i, j, n - 1 - i - j), a)); } for (int i = 0; i <= n - 1; i++) if (i * k1 <= n - 1 - k2) { int res = n - 1 - k2 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n - 1) continue; Inc(Sum, mul(Polycoef(i, j, n - 1 - i - j), b)); } Mul(tot, Inv(n)); return printf("%d\n", mul(Sum, Inv(tot))) * 0; }
-
0
#include <bits/stdc++.h> using namespace std;
const int N = 1e7 + 5, mod = 998244353;
inline int inc(int a, int b) { return (a += b - mod) + (a >> 31 & mod); } inline int dec(int a, int b) { return (a -= b) + (a >> 31 & mod); } inline int mul(int a, int b) { return 1ll * a * b % mod; } inline void Inc(int &a, int b) { return void(a = inc(a, b)); } inline void Dec(int &a, int b) { return void(a = dec(a, b)); } inline void Mul(int &a, int b) { return void(a = mul(a, b)); } inline int Pow(int a, int b) { int c = 1; for (; b; Mul(a, a), b >>= 1) if (1 & b) Mul(c, a); return c; } inline int Inv(int x) { return Pow(x, mod - 2); }
int fac[N], inv[N], n, k1, k2, a, b, tot, Sum;
inline int Polycoef(int n, int m, int k) { return mul(fac[n + m + k], mul(mul(inv[n], inv[m]), inv[k])); }
int main(void) { scanf("%d%d%d%d%d", &k1, &k2, &n, &a, &b); fac[0] = 1; for (int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i); inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = mul(mod - mod / i, inv[mod % i]); inv[0] = 1; for (int i = 1; i <= n; i++) Mul(inv[i], inv[i - 1]); for (int i = 0; i <= n; i++) if (i * k1 <= n - 1) { int res = n - 1 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n) continue; Inc(tot, Polycoef(i, j, n - i - j)); } for (int i = 0; i <= n - 1; i++) if (i * k1 <= n - 1 - k1) { int res = n - 1 - k1 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n - 1) continue; Inc(Sum, mul(Polycoef(i, j, n - 1 - i - j), a)); } for (int i = 0; i <= n - 1; i++) if (i * k1 <= n - 1 - k2) { int res = n - 1 - k2 - i * k1; if (res % k2) continue; int j = res / k2; if (j > n - 1) continue; Inc(Sum, mul(Polycoef(i, j, n - 1 - i - j), b)); } Mul(tot, Inv(n)); return printf("%d\n", mul(Sum, Inv(tot))) * 0; }
- 1
信息
- ID
- 119
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 74
- 已通过
- 45
- 上传者