Solution -「洛谷 P5827」边双连通图计数
\(\mathcal{Description}\)
link.
求包含 \(n\) 个点的边双连通图的个数。
\(n\le10^5\)。
\(\mathcal{Solution}\)
类似于这道题,仍令 \(D(x)\) 为有根无向连通图的 \(\text{EGF}\),\(B(x)\) 为边双连通图的 \(\text{EGF}\),考虑用 \(B\) 表示 \(D\)。显然,根仅存在于一个边双连通分量中。我们可以在这个连通分量的任意一个点上挂一个有根连通图,这显然不会影响当前的边双,\(\text{EGF}\) 为 \(nD(x)\)(\(n\) 为根所在边双大小。)事实上可以挂许多图,所以处根所在边双以外的 \(\text{EGF}\) 为 \(\exp\left(nD(x)\right)\)。枚举根所在边双大小,就有:
\]
令 \(F(x)=x\exp D(x)\),\(F^{-1}(x)\) 为其复合逆。将 \(F^{-1}(x)\) 代入上式得:
\]
扩展拉格朗日反演:
\]
重新展开 \(F\),化简得:
\]
求出 \(D\),继而求出 \([x^n]B(x)\)。复杂度 \(\mathcal O(n\log n)\)。
\(\mathcal{Code}\)
#include <cmath>
#include <cstdio>
const int MAXN = 1 << 18, MOD = 998244353;
int n, fac[MAXN + 5], ifac[MAXN + 5], inv[MAXN + 5];
int F[MAXN + 5], G[MAXN + 5], H[MAXN + 5], T[MAXN + 5];
inline int qkpow ( int a, int b, const int p = MOD ) {
int ret = 1;
for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
return ret;
}
namespace Poly {
const int G = 3;
inline void NTT ( const int n, int* A, const int tp ) {
static int lstn = -1, rev[MAXN + 5] {};
if ( lstn ^ n ) {
int lgn = log ( n ) / log ( 2 ) + 0.5;
for ( int i = 0; i < n; ++ i ) rev[i] = ( rev[i >> 1] >> 1 ) | ( ( i & 1 ) << lgn >> 1 );
lstn = n;
}
for ( int i = 0; i < n; ++ i ) if ( i < rev[i] ) A[i] ^= A[rev[i]] ^= A[i] ^= A[rev[i]];
for ( int i = 2, stp = 1; i <= n; i <<= 1, stp <<= 1 ) {
int w = qkpow ( G, ( MOD - 1 ) / i );
if ( ! ~ tp ) w = qkpow ( w, MOD - 2 );
for ( int j = 0; j < n; j += i ) {
for ( int k = j, r = 1; k < j + stp; ++ k, r = 1ll * r * w % MOD ) {
int ev = A[k], ov = 1ll * r * A[k + stp] % MOD;
A[k] = ( ev + ov ) % MOD, A[k + stp] = ( ev - ov + MOD ) % MOD;
}
}
}
if ( ! ~ tp ) for ( int i = 0; i < n; ++ i ) A[i] = 1ll * A[i] * inv[n] % MOD;
}
inline void polyDer ( const int n, const int* A, int* R ) {
for ( int i = 1; i < n; ++ i ) R[i - 1] = 1ll * i * A[i] % MOD;
R[n - 1] = 0;
}
inline void polyInt ( const int n, const int* A, int* R ) {
for ( int i = n - 1; ~ i; -- i ) R[i + 1] = 1ll * inv[i + 1] * A[i] % MOD;
R[0] = 0;
}
inline void polyInv ( const int n, const int* A, int* R ) {
static int tmp[2][MAXN + 5] {};
if ( n == 1 ) return void ( R[0] = qkpow ( A[0], MOD - 2 ) );
polyInv ( n >> 1, A, R );
for ( int i = 0; i < n; ++ i ) tmp[0][i] = A[i], tmp[1][i] = R[i];
NTT ( n << 1, tmp[0], 1 ), NTT ( n << 1, tmp[1], 1 );
for ( int i = 0; i < n << 1; ++ i ) tmp[0][i] = 1ll * tmp[0][i] * tmp[1][i] % MOD * tmp[1][i] % MOD;
NTT ( n << 1, tmp[0], -1 );
for ( int i = 0; i < n; ++ i ) R[i] = ( 2ll * R[i] % MOD - tmp[0][i] + MOD ) % MOD;
for ( int i = 0; i < n << 1; ++ i ) tmp[0][i] = tmp[1][i] = 0;
}
inline void polyLn ( const int n, const int* A, int* R ) {
static int tmp[2][MAXN + 5] {};
polyDer ( n, A, tmp[0] ), polyInv ( n, A, tmp[1] );
NTT ( n << 1, tmp[0], 1 ), NTT ( n << 1, tmp[1], 1 );
for ( int i = 0; i < n << 1; ++ i ) tmp[0][i] = 1ll * tmp[0][i] * tmp[1][i] % MOD;
NTT ( n << 1, tmp[0], -1 ), polyInt ( n << 1, tmp[0], R );
for ( int i = 0; i < n << 1; ++ i ) tmp[0][i] = tmp[1][i] = 0;
}
inline void polyExp ( const int n, const int* A, int* R ) {
static int tmp[MAXN + 5] {};
if ( n == 1 ) return void ( R[0] = 1 );
polyExp ( n >> 1, A, R ), polyLn ( n, R, tmp );
tmp[0] = ( A[0] + 1 - tmp[0] + MOD ) % MOD;
for ( int i = 1; i < n; ++ i ) tmp[i] = ( A[i] - tmp[i] + MOD ) % MOD;
NTT ( n << 1, tmp, 1 ), NTT ( n << 1, R, 1 );
for ( int i = 0; i < n << 1; ++ i ) R[i] = 1ll * R[i] * tmp[i] % MOD;
NTT ( n << 1, R, -1 );
for ( int i = n; i < n << 1; ++ i ) R[i] = tmp[i] = 0;
}
} // namespace Poly.
inline void init () {
int len = MAXN >> 1;
inv[1] = fac[0] = ifac[0] = fac[1] = ifac[1] = 1;
for ( int i = 2; i <= MAXN; ++ i ) {
fac[i] = 1ll * i * fac[i - 1] % MOD;
inv[i] = 1ll * ( MOD - MOD / i ) * inv[MOD % i] % MOD;
ifac[i] = 1ll * inv[i] * ifac[i - 1] % MOD;
}
for ( int i = 0; i < len; ++ i ) F[i] = 1ll * qkpow ( 2, ( i * ( i - 1ll ) >> 1 ) % ( MOD - 1 ) ) * ifac[i] % MOD;
Poly::polyLn ( len, F, G );
for ( int i = 0; i < len; ++ i ) G[i] = 1ll * G[i] * i % MOD;
Poly::polyDer ( len, G, H ), Poly::NTT ( MAXN, H, 1 );
}
inline void solve () {
int len = MAXN >> 1;
if ( n == 1 ) return void ( puts ( "1" ) );
for ( int i = 0; i < MAXN; ++ i ) F[i] = T[i] = 0;
for ( int i = 0; i < len; ++ i ) F[i] = 1ll * ( MOD - n ) % MOD * G[i] % MOD;
Poly::polyExp ( len, F, T ), Poly::NTT ( MAXN, T, 1 );
for ( int i = 0; i < MAXN; ++ i ) F[i] = 1ll * T[i] * H[i] % MOD;
Poly::NTT ( MAXN, F, -1 );
printf ( "%d\n", int ( 1ll * inv[n] * fac[n - 1] % MOD * F[n - 1] % MOD ) );
}
int main () {
init ();
for ( int i = 1; i <= 5; ++ i ) scanf ( "%d", &n ), solve ();
return 0;
}
Solution -「洛谷 P5827」边双连通图计数的更多相关文章
- Solution -「洛谷 P5827」点双连通图计数
\(\mathcal{Description}\) link. 求有 \(n\) 个结点的点双连通图的个数,对 \(998244353\) 取模. \(n\le10^5\). \(\mat ...
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Note/Solution -「洛谷 P5158」「模板」多项式快速插值
\(\mathcal{Description}\) Link. 给定 \(n\) 个点 \((x_i,y_i)\),求一个不超过 \(n-1\) 次的多项式 \(f(x)\),使得 \(f(x ...
- Solution -「洛谷 P5236」「模板」静态仙人掌
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的仙人掌,\(q\) 组询问两点最短路. \(n,q\le10^4\),\(m\ ...
- Solution -「洛谷 P4198」楼房重建
\(\mathcal{Description}\) Link. 给定点集 \(\{P_n\}\),\(P_i=(i,h_i)\),\(m\) 次修改,每次修改某个 \(h_i\),在每次修改后 ...
- Solution -「洛谷 P6577」「模板」二分图最大权完美匹配
\(\mathcal{Description}\) Link. 给定二分图 \(G=(V=X\cup Y,E)\),\(|X|=|Y|=n\),边 \((u,v)\in E\) 有权 \(w( ...
- Solution -「洛谷 P6021」洪水
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个点的带点权树,删除 \(u\) 点的代价是该点点权 \(a_u\).\(m\) 次操作: 修改单点点权. ...
- Solution -「洛谷 P4719」「模板」"动态 DP" & 动态树分治
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个结点的带权树,\(m\) 次单点点权修改,求出每次修改后的带权最大独立集. \(n,m\le10^5 ...
- Solution -「洛谷 P4320」道路相遇
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的连通无向图,并给出 \(q\) 个点对 \((u,v)\),询问 \(u\) 到 ...
随机推荐
- Linux上天之路系列目录
Linux上天之路系列目录 Linux上天之路(一)之Linux前世今生 Linux上天之路(二)之Linux安装 Linux上天之路(三)之Linux系统目录 Linux上天之路(四)之Linux界 ...
- Spark案例练习-UV的统计
关注公众号:分享电脑学习回复"百度云盘" 可以免费获取所有学习文档的代码(不定期更新)云盘目录说明:tools目录是安装包res 目录是每一个课件对应的代码和资源等doc 目录是一 ...
- 网络协议学习笔记(二)物理层到MAC层,交换机和VLAN,ICMP与ping原理
概述 之前网络学习笔记主要讲解了IP的诞生,或者说整个操作系统的诞生,一旦有了IP,就可以在网络的环境里和其他的机器展开沟通了.现在开始给大家讲解关于网络底层的相关知识. 从物理层到MAC层:如何在宿 ...
- RHCSA 第四天
1.使用whereis 查找 locate命令 使用which查找whereis命令 使用locate查找rm命令 2.find命令使用: 使用find命令在当前路径下查找所有的普通文件 使用f ...
- k8s的应用包管理工具helm的部署和使用
1.概述 我们一般是在k8s里面部署一些简单的应用,比如用deployment,daemonset,statefuleset的方式来部署应用,但是如果要部署一些复杂的应用,那么整个配置的编写.部署的过 ...
- 触发器中获取sql
CREATE trigger 触发器名 on 表名 for update,delete as set nocount on create table #t(EvebtType varchar(60), ...
- Java 异步 I/O
Java 中的异步 I/O 简称 AIO, A 即 Asynchronous.AIO 在 JDK1.7 时引入,基于操作系统提供的异步 I/O 通信模型,封装了一些进行异步 I/O 操作的 API. ...
- ThinkPad S5立体声混响以及语音识别
smartaudio里面改成语音识别就可以是立体声混响了.但是微软语音识别在国内依然不好用,微软服务在国内太卡了. (联想总是多此一举,各种乱起八糟的软件,给用户造成困难,以前老机子驱动无线网卡锁在L ...
- 一种Django多租户解决方案
什么是多租户? 多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性. 多租 ...
- gin中从reader读取数据数据
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" &quo ...