考虑以下生成大小为 $n$ 的随机树的方法:
取所有 $n(n - 1)/2$ 条可能的边,并对它们进行均匀随机排列。从空图开始,按照选定的顺序遍历这些边,如果某条边连接了两个不同的连通分量,则将其加入图中。最终,你将得到一棵树。
对于所有从 $2$ 到给定 $N$ 的 $n$,计算该算法生成一条竹节(bamboo)的概率,结果对给定的 $P$ 取模。保证 $P$ 是素数且 $P \pmod{2^{16}} = 1$。
回想一下,竹节是指所有顶点的度数均不超过 $2$ 的树。
由于我们给出了模数,你可能需要使用 Barrett 约减(https://en.wikipedia.org/wiki/Barrett_reduction)。它允许你在 $P$ 为常数但编译时未知的情况下,比通常情况更快地计算 $x \pmod P$。KACTL 实现的链接及附加信息:https://github.com/kth-competitive-programming/kactl/blob/main/content/various/FastMod.h。
代码:
using u32 = unsigned int;
using u64 = unsigned long long;
using u128 = __uint128_t;
struct Barrett {
u64 b, m;
Barrett() : b(), m() {}
Barrett(u64 _b) : b(_b), m(-1ULL / _b) {}
u32 reduce(u64 x) {
u64 q = (u64)((u128(m) * x) >> 64), r = x - q * b;
return r - b * (r >= b);
}
} BA;
用法示例:
u32 mult(u32 x, u32 y) {
return BA.reduce((u64)x * y);
}
int main() {
int n, P;
cin >> n >> P;
BA = Barrett(P);
cout << mult(123456, 7890123) << endl;
return 0;
}
在使用 Barrett 之前,别忘了构造一个实例。我们不保证此问题在不使用 Barrett 约减或其他快速乘法方法的情况下可解。
输入格式
仅一行,包含两个整数 $N$ 和 $P$ ($2 \le N \le 250, 2 < P < 10^9$)。保证 $P$ 是素数且 $P \pmod{2^{16}} = 1$。
输出格式
输出 $N - 1$ 个整数,即对于所有从 $2$ 到 $N$ 的 $n$ 的答案。
回想一下,如果概率可以表示为不可约分数 $u/v$,且 $v$ 与 $P$ 互质,则该概率对 $P$ 取模的结果为 $(u \cdot v^{-1}) \pmod P$,其中 $v^{-1}$ 是 $v$ 在模 $P$ 意义下的乘法逆元。
样例
输入 1
10 998244353
输出 1
1 1 532396989 328786831 443364983 567813846 34567523 466373946 474334062