讲题人:“这是一个很经典的模型,大家应该都会”

我:“???”


# 题面

给出 \(m\),求所有 \(m\) 个点的有标号强联通竞赛图的哈密顿回路数量的平均数。答案对 \(998244353\) 取模。

输入 \(n\),对每个 \(m=1\sim n\) 求解。

数据规模:\(n\le10^5\)。


# 解析

问题分成两个部分:

  • 所有 \(m\) 个点的竞赛图的哈密顿回路数量;注意到只有强联通的竞赛图有哈密顿回路,所以计算时不用管强联通;
  • \(m\) 个点的强联通竞赛图数量。

第一个部分。我们发现对一个竞赛图计算它的哈密顿回路没法做,考虑对一个哈密顿回路计算它出现在多少个竞赛图中

哈密顿回路从 \(1\) 处断开,就是一个从 \(1\) 开头的 \(1\sim m\) 的排列,数量为 \((m-1)!\)。我们钦定这样一条哈密顿回路在竞赛图上,相当于已经给 \(m\) 条边钦定了方向,剩下的 \(\frac{m(m-1)}2-m\) 条边随便定向,即哈密顿回路总数为:

\[2^{\frac{m(m-1)}{2}-m}\times(m-1)!
\]

特判一下 \(n\le2\)。

第二个部分。记 \(f_i\) 表示 \(i\) 个点的强联通竞赛图的数量,我们要计算 \(f_1\sim f_n\)。计算这些值,可以考虑生成函数 —— 注意到有标号,记 \(f_i\) 的 EGF 为 \(F(x)\)。

直接限制强联通并不好算,于是正难则反。计算一定没有强联通的竞赛图的数量。

重要性质

将竞赛图强联通缩点后的图记为 \(T\),对 \(T\) 做拓扑排序,则按拓扑序排列的点在 \(T\) 上形成一条链。

如果竞赛图不是强联通,则一定存在多个强联通分量,结合上述性质,可以找到拓扑序最小的一个强联通分量 \(G_1\),而剩下的图 \(G_2\) 是一个任意的竞赛图。由于 \(G_1\) 是极大的强联通分量,且拓扑序最小,则 \(G_1\) 和 \(G_2\) 之间的所有边都是从 \(G_1\) 连向 \(G_2\)。

我们可以枚举 \(G_1\) 的大小为 \(i\),则 \(G_1\) 是大小为 \(i\) 的强联通竞赛图,而 \(G_2\) 为大小为 \(m-i\) 的任意竞赛图。记 \(g_i\) 为 \(i\) 个点的任意竞赛图数量。可以写出转移式:

\[f_s=g_s-\sum_{i=1}^{s-1}\binom{s}{i}f_ig_{s-i}
\]

有组合数,能够拆成 EGF 的形式。记 \(G(x)\) 为 \(g_i\) 的 EGF:

\[\begin{aligned}
\frac{f_s}{s!}=\frac{g_s}{s!}-\sum_{i=1}^{s-1}\frac{f_i}{i!}\times\frac{g_{s-i}}{(s-i)!}\\
F(x)=G(x)-F(x)G(x)
\end{aligned}
\]

分治 NTT 或者多项式求逆,但是感觉多项式求逆好写些……


# 源代码

  1. /*Lucky_Glass*/
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <algorithm>
  5. using namespace std;
  6. const int MOD = 998244353, N = 1e5 + 10, L = 262144;
  7. #define con(typ) const typ &
  8. inline int add(int a, con(int) b) {
  9. return (a += b) >= MOD ? a - MOD : a;
  10. }
  11. inline int sub(int a, con(int) b) {
  12. return (a -= b) < 0 ? a + MOD : a;
  13. }
  14. inline int mul(con(int) a, con(int) b) {
  15. return int(1ll * a * b % MOD);
  16. }
  17. inline int iPow(int a, int b) {
  18. int r = 1;
  19. while ( b ) {
  20. if ( b & 1 ) r = mul(a, r);
  21. a = mul(a, a), b >>= 1;
  22. }
  23. return r;
  24. }
  25. namespace BASICPOLY {
  26. int rev[L + 10], elg2[L + 10], powg[L + 10];
  27. void init() {
  28. elg2[1] = 0, powg[0] = 1, powg[1] = iPow(3, (MOD - 1) >> 18);
  29. for (int i = 2; i <= L; i++) {
  30. elg2[i] = elg2[(i + 1) >> 1] + 1;
  31. powg[i] = mul(powg[i - 1], powg[1]);
  32. }
  33. }
  34. void ntt(int *arr, con(int) len, con(int) typ) {
  35. for (int i = 1; i < len; i++) {
  36. rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (len >> 1) : 0);
  37. if ( i < rev[i] ) swap(arr[i], arr[rev[i]]);
  38. }
  39. for (int i = 1, ii = 2; i < len; i <<= 1, ii <<= 1) {
  40. int s = L >> elg2[ii];
  41. for (int j = 0; j < len; j += ii) {
  42. int *a = arr + j, *b = a + i, *p = powg, q = *b;
  43. for (int k = 0; k < i; k++, a++, q = mul(*(p += s), *(++b)))
  44. *b = sub(*a, q), *a = add(*a, q);
  45. }
  46. }
  47. if ( typ == -1 ) {
  48. reverse(arr + 1, arr + len);
  49. int ivn = MOD - ((MOD - 1) >> elg2[len]);
  50. if ( mul(ivn, len) != 1 ) printf("???");
  51. for (int i = 0; i < len; i++) arr[i] = mul(arr[i], ivn);
  52. }
  53. }
  54. int ta[L + 10], tb[L + 10];
  55. void polyMul(int *a, int *b, con(int) la, con(int) lb, int *r,
  56. con(int) fr = -1) {
  57. int lr = la + lb - 1, len = 1 << elg2[lr];
  58. for (int i = 0; i < la; i++) ta[i] = a[i];
  59. for (int i = la; i < len; i++) ta[i] = 0;
  60. for (int i = 0; i < lb; i++) tb[i] = b[i];
  61. for (int i = lb; i < len; i++) tb[i] = 0;
  62. ntt(ta, len, 1), ntt(tb, len, 1);
  63. for (int i = 0; i < len; i++) ta[i] = mul(ta[i], tb[i]);
  64. ntt(ta, len, -1);
  65. for (int i = 0, ii = ~fr ? fr : lr; i < ii; i++) r[i] = ta[i];
  66. }
  67. void polyInv(int *a, int *r, con(int) len) {
  68. if ( len == 1 ) {
  69. r[0] = iPow(a[0], MOD - 2);
  70. return;
  71. }
  72. polyInv(a, r, (len + 1) >> 1);
  73. int llen = 1 << elg2[len << 1];
  74. for (int i = 0; i < len; i++) ta[i] = a[i];
  75. for (int i = len; i < llen; i++) ta[i] = 0;
  76. for (int i = 0, ii = (len + 1) >> 1; i < ii; i++) tb[i] = r[i];
  77. for (int i = (len + 1) >> 1; i < llen; i++) tb[i] = 0;
  78. ntt(ta, llen, 1), ntt(tb, llen, 1);
  79. for (int i = 0; i < llen; i++)
  80. ta[i] = mul(tb[i], sub(2, mul(ta[i], tb[i])));
  81. ntt(ta, llen, -1);
  82. for (int i = 0; i < len; i++) r[i] = ta[i];
  83. }
  84. }
  85. int fac[N], ifac[N], ara[N], arb[N];
  86. int calc(con(int) n) {
  87. if ( n <= 2 ) return n == 1;
  88. return mul(fac[n - 1], iPow(2, (n * (n - 1ll) / 2 - n) % (MOD - 1)));
  89. }
  90. int funS(con(int) n) {
  91. return iPow(2, n * (n - 1ll) / 2 % (MOD - 1));
  92. }
  93. void init() {
  94. fac[0] = 1;
  95. for (int i = 1; i < N; i++)
  96. fac[i] = mul(fac[i - 1], i);
  97. ifac[N - 1] = iPow(fac[N - 1], MOD - 2);
  98. for (int i = N - 2; ~i; i--)
  99. ifac[i] = mul(ifac[i + 1], i + 1);
  100. BASICPOLY::init();
  101. for (int i = 1; i < N; i++) ara[i] = mul(funS(i), ifac[i]);
  102. ara[0]++;
  103. BASICPOLY::polyInv(ara, arb, N);
  104. ara[0]--;
  105. BASICPOLY::polyMul(ara, arb, N, N, ara, N);
  106. for (int i = 1; i < N; i++) ara[i] = mul(ara[i], fac[i]);
  107. }
  108. int main() {
  109. init();
  110. int n;
  111. scanf("%d", &n);
  112. for (int i = 1; i <= n; i++) {
  113. if ( ara[i] )
  114. printf("%d\n", mul(calc(i), iPow(ara[i], MOD - 2)));
  115. else printf("-1\n");
  116. }
  117. return 0;
  118. }

THE END

Thanks for reading!

你喜欢海风咸咸的气息

踩着湿湿的沙砾

你说人们的骨灰应该撒进海里

你问我死后会去哪里

有没有人爱你

世界能否不再

——《海底(Cover)》 By 祖娅纳惜

「SOL」射命丸文的笔记 (洛谷)的更多相关文章

  1. loj2734「JOISC 2016 Day 2」女装大佬 || 洛谷P3615 如厕计划

    loj2734 洛谷P3615 http://218.5.5.242:9021/problem/185 不会做... 题解(来自ditoly): 这一步更详细的解释(来自kkksc03): 还是从后面 ...

  2. 斜率优化dp学习笔记 洛谷P3915[HNOI2008]玩具装箱toy

    本文为原创??? 作者写这篇文章的时候刚刚初一毕业…… 如有错误请各位大佬指正 从例题入手 洛谷P3915[HNOI2008]玩具装箱toy Step0:读题 Q:暴力? 如果您学习过dp 不难推出d ...

  3. 边带权并查集 学习笔记 & 洛谷P1196 [NOI2002] 银河英雄传说 题解

    花了2h总算把边带权并查集整明白了qaq 1.边带权并查集的用途 众所周知,并查集擅长维护与可传递关系有关的信息.然而我们有时会发现并查集所维护的信息不够用,这时"边带权并查集"就 ...

  4. 解题笔记-洛谷-P1010 幂次方

    0 题面 题目描述 任何一个正整数都可以用2的幂次方表示.例如 137=2^7+2^3+2^0 同时约定方次用括号来表示,即a^b 可表示为a(b). 由此可知,137可表示为: 2(7)+2(3)+ ...

  5. 快速沃尔什变换(FWT)学习笔记 + 洛谷P4717 [模板]

    FWT求解的是一类问题:\( a[i] = \sum\limits_{j\bigoplus k=i}^{} b[j]*c[k] \) 其中,\( \bigoplus \) 可以是 or,and,xor ...

  6. [折腾笔记] 洛谷P1149-火柴棒等式 AC记

    原题链接: https://www.luogu.org/problem/P1149 题面简述: 给你n根火柴棍,你可以拼出多少个形如"A+B=C""A+B=C" ...

  7. STL Queue(队列)学习笔记 + 洛谷 P1540 机器翻译

    队(Queue) 队简单来说就是一个先进先出的“栈”,但是不同于标准“栈”的先进后出. 基本操作: push(x) 将x压入队列的末端 pop() 弹出队列的第一个元素(队顶元素),注意此函数并不返回 ...

  8. STL Stack(栈)学习笔记 + 洛谷 P1449 后缀表达式

    稍微看了看刘汝佳的白皮书,“实用主义”的STL实在是香到我了,而且在实验室大佬的推荐下我开始了stl的学习. 每篇附带一个题目方便理解,那行,直接开始. 毕竟是实用主义,所以就按照给的题目的例子来理解 ...

  9. 「LGR-049」洛谷7月月赛 D.Beautiful Pair

    「LGR-049」洛谷7月月赛 D.Beautiful Pair 题目大意 : 给出长度为 \(n\) 的序列,求满足 \(i \leq j\) 且 $a_i \times a_j \leq \max ...

  10. 「P4994」「洛谷11月月赛」 终于结束的起点(枚举

    题目背景 终于结束的起点终于写下句点终于我们告别终于我们又回到原点…… 一个个 OIer 的竞赛生涯总是从一场 NOIp 开始,大多也在一场 NOIp 中结束,好似一次次轮回在不断上演.如果这次 NO ...

随机推荐

  1. dcloud打包安卓隐私政策上架问题!

    友情链接: https://ask.dcloud.net.cn/article/36937 两种方式: 方式1. 使用dcloud的方式,在打包文件中配置 隐私json文件,使用dclould封装的隐 ...

  2. 16.SharedPreferences存储

    1.SharedPreferences存储 不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的,保存为.xml文件. 也就是说当保存一条数据的时候,需要给这条数据提 ...

  3. 【服务器数据恢复】ZFS文件系统下ZPOOL下线的数据恢复案例

    服务器数据恢复环境:SUN ZFS系列某型号存储阵列:40块磁盘组建的存储池(其中4块磁盘用作全局热备盘),池内划分出若干空间映射到服务器使用:服务器使用Windows操作系统. 服务器故障:服务器在 ...

  4. pillow 创建图片并添加一些自定义信息

    from PIL import Image vm = Image.new('RGBA', (dshape[1], dshape[0])) vm = Image.fromarray(np.array(s ...

  5. 关于Salesforce存在至于项目的选择List的取值问题

    概要: 我们在做项目的时候,经常会遇到一个问题: 一个选择List字段的可选项被另一个选择List制约,这种情况如何在后台取得这两者的对应关系. 原文在这里(侵删): Apexで連動項目の選択肢を取得 ...

  6. 浅谈组件二封-vue

    目录 组件二封不是换一种写法 组件二封应当具备哪些条件 我认为的二封应当有哪些作用 二封的好处 先来一个列表页demo来看看效果(Vue2) 本文仅仅针对vue系列做探讨, 项目倾向于大量增删改查的后 ...

  7. java中取整数绝对值_Java之——位运算求整数绝对值通过下面的位运算可以得到一个整数的绝对值

    public int abs( int a ) {return (a + (a >> 31)) ^ (a >> 31) ;//前半部分-1或+0,后半部分取反 } a为正数的情 ...

  8. matlab判断操作

    类型判断 1.查看变量类型时可用class,判断某变量的类型值:会生成0或1,1-匹配,0-不匹配 isa(Data,'double') isa(Data,'cell') 2.也可用如下. strcm ...

  9. 读取数组树下的某值,并返回其父级下的任何值 vue

    1 // 遍历树 获取对应 id的项中的值 2 queryTree(tree, value) { 3 let stark = []; 4 stark = stark.concat(tree); 5 w ...

  10. redis-RedisTemplate.opsForValue 常用方法

    16.multiSetIfAbsent(Map<? extends K,? extends V> map) 如果对应的map集合名称不存在,则添加,如果存在则不做修改. Map value ...