「UOJ498」新年的追逐战

题目

点这里看题目。

分析

首先,我们不妨考察 \(n=1\) 的情况。如果认为 \(F(x)\) 为连通无向图的 EGF,则事实上,我们可以直接考虑任意一个连通块和剩下的方案数,连通块个数的 EGF \(C(x)\) 为:

\[C=F\exp F \]

考察 \(n>1\)​ 的情况,不妨从 \(n=2\)​ 的简单情况入手。首先,我们给 \(G_1\times G_2\)​ 赋一个组合含义:我们可以认为是同时在两个图上面进行游走,且每次两个图上都必须经过一条边。那么,我们考察连通块,也就是考察可以经过若干步游走到达的等价类

首先考察一种平凡的移动方式。在图 \(G_1\times G_2\) 上面,我们如果要从 \((u_1,v_1)\) 出发,则:

  1. 如果 \(u_1\) 或者 \(v_1\) 没有邻接点,那么这个状态动不了了。😓

  2. 否则,我们假设目标状态为 \((u_2,v_2)\)。之后,我们可以先让 \(u_1\) 移动到 \(u_2\),在这个过程中 \(v_1\) 在一条邻接边上反复横跳;之后移动 \(v_1\)\(v_2\),这个过程中 \(u_1\) 也需要反复横跳。

第一种情况是平凡的。第二种情况下,我们首先得保证 \(u_1,u_2\)​ 和 \(v_1,v_2\)​ 得各自同一连通块中;其次,\(u_1\rightarrow u_2\)​ 和 \(v_1\rightarrow v_2\)​ 的路径奇偶性必须相同。这导向了二分性的考察:如果某一个连通块不是二分图,则我们可以找到奇环,相应地这一条路径的奇偶性可以随便改变,否则奇偶性就不能随便改变(这容易证明)。因此,在第二种情况下,如果我们考察两个连通块的乘积中连通块的个数,则这仅仅和两者是否都是二分图有关。

补充:虽然这仅仅是一种平凡的移动方式,不过在二分图上,可以发现这种移动方式能够到达的状态就是所有能够到达的状态(任何环都是偶环)。而在非二分图上——我们已经讨论过了。

进一步推广到 \(n\) 个图的乘积 \(G=\prod_kG_k\)。我们可以从每个因子 \(G_k\) 取出一个连通块 \(C_k\),再考虑:

  1. 如果存在大小为 1 的连通块,则此时对 \(G\) 的贡献为 \(\prod_k|V(C_k)|\)

  2. 否则,如果连通块中有 \(b\) 个二分图,则对 \(G\) 的贡献为 \(2^{\max\{b-1,0\}}\)

    幂次为 \(b-1\) 的理由是,等价类中的状态只需要保证“相对奇偶性”相同即可,因此需要去掉一个 2 的因子。

之后的过程实际上是纯组合的问题。由于所有的 \(m\) 均已给出,我们可以规避不必要的生成函数。事实上我们只需要分别考虑:

  1. 存在大小为 1 的连通块,此时 \(G_k\) 如果选出了大小为 1 的连通块,则贡献为 \(a_k=m_k2^{\binom{m_k-1}{2}}\),否则随便选,贡献为 \(b_k=[x^{m_k}]xC’(x)\)。注意这里需要计算连通块的大小。

  2. 所有连通块大小均大于 1。此时每个二分图贡献 2,而每个非二分图贡献 1。如果设连通二分图的 EGF 为 \(B(x)\),则我们只需要知道 \(c_k=[x^{m_k}]B\exp F\)\(d_k=[x^{m_k}](F-B)\exp F\)

最终的答案不难得出,即为:

\[\prod_{k=1}^nb_k-\prod_{k=1}^na_k+\frac{1}{2}\left(\prod_{k=1}^n(2c_k+d_k)+\prod_{k=1}^nd_k\right) \]


最后考虑一下 \(B(x)\) 怎么求。如果不考虑计算重复的问题,我们可以直接有序地枚举二分图的一部,得到:

\[H(x)=\sum_{n\ge 0}\frac{x^n}{n!}\sum_{k=0}^n\binom{n}{k}2^{nk} \]

而我们注意到,对于一个连通二分图,枚举二分图的一部实际上就是考虑所有的染色方案,而实际上只会有两种不同的染色方案(本质上就是对换黑白)。更进一步地,如果有 \(k\) 个二分连通块,染色方案就是 \(2^k\) 种。因此,我们可以得到:

\[H=\exp(2B) \]

算一个 \(\ln\) 即可得到 \(B(x)\)

补充:据说这样的图计数问题和某种“图论生成函数”有关。

定义序列 \(\{a_n\}_{n\ge 0}\) 的 GGF 为:

\[\sum_{n\ge 0}\frac{a_n}{n!2^{\binom{n}{2}}}x^n \]

两个 GGF 的卷积,表示的就是“任意选择两个图,将它们的标号合并,并在二者之间任意连边”后的 GGF。


比如,我们可以记录 \(S(x)\) 为序列 \(\{1\}_{n\ge 0}\) 的 GGF,则这里的 \(H(x)\) 即为 \(S^2(x)\)

小结:

  1. 连通块就是可以互相到达的等价类,这样的观点可以记录一下。

    此后的分析都基于这一点展开,相对来说就不是很难了。

  2. 后面这个 GGF 有点意思,不知道哪里有更加深入的讲解?

  3. 最后计算 \(B\)​ 时,用到的二分图染色的计数性质,也可以记录一下。

代码

#include <cstdio>
#include <algorithm>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 998244353, inv2 = ( mod + 1 ) >> 1;
const int MAXN = ( 1 << 18 ) + 5;

template<typename _T>
void read( _T &x ) {
	x = 0; char s = getchar(); int f = 1;
	while( ! ( '0' <= s && s <= '9' ) ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + '0' );
}

template<typename _T>
inline _T Min( const _T &a, const _T &b ) {
	return a < b ? a : b;
}

template<typename _T>
inline _T Max( const _T &a, const _T &b ) {
	return a > b ? a : b;
}

int F[MAXN], G[MAXN];
int fac[MAXN], ifac[MAXN], pw2[MAXN][3], pwInv[MAXN][3];

int A[MAXN], B[MAXN], C[MAXN], D[MAXN];

int siz[MAXN];

int N, M;

inline int Qkpow( int, int );
inline int Inv( const int &a ) { return Qkpow( a, mod - 2 ); } 
inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }

inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; }

inline int Qkpow( int base, int indx ) {
	int ret = 1;
	while( indx ) {
		if( indx & 1 ) MulEq( ret, base );
		MulEq( base, base ), indx >>= 1;
	}
	return ret;
}

namespace Basics {
	const int L = 18, phi = mod - 1, g = 3;

	int w[MAXN];

	inline void NTTInit( const int &n = 1 << L ) {
		w[0] = 1, w[1] = Qkpow( g, phi >> L );
		rep( i, 2, n - 1 ) w[i] = Mul( w[i - 1], w[1] );
	}

	inline void DIF( int *coe, const int &n ) {
		int *wp, p, e, o;
		for( int s = n >> 1 ; s ; s >>= 1 )
			for( int i = 0 ; i < n ; i += s << 1 ) {
				p = ( 1 << L ) / ( s << 1 ), wp = w;
				for( int j = 0 ; j < s ; j ++, wp += p ) {
					e = coe[i + j], o = coe[i + j + s];
					coe[i + j] = Add( e, o );
					coe[i + j + s] = Mul( *wp, Sub( e, o ) );
				}
			}
	}

	inline void DIT( int *coe, const int &n ) {
		int *wp, p, k;
		for( int s = 1 ; s < n ; s <<= 1 )
			for( int i = 0 ; i < n ; i += s << 1 ) {
				p = ( 1 << L ) / ( s << 1 ), wp = w;
				for( int j = 0 ; j < s ; j ++, wp += p )
					k = Mul( *wp, coe[i + j + s] ),
					coe[i + j + s] = Sub( coe[i + j], k ),
					coe[i + j] = Add( coe[i + j], k );
			}
		std :: reverse( coe + 1, coe + n );
		int inv = Inv( n ); rep( i, 0, n - 1 ) MulEq( coe[i], inv );
	}

	inline void PolyDer( int *coe, const int &n ) {
		rep( i, 0, n - 2 ) coe[i] = Mul( coe[i + 1], i + 1 );
		coe[n - 1] = 0;
	}

	inline void PolyInt( int *coe, const int &n ) {
		per( i, n, 1 ) coe[i] = Mul( coe[i - 1], Inv( i ) );
		coe[0] = 0;
	}
}

namespace PolyMul {
	int P[MAXN], Q[MAXN];

	void PolyMul( int *ret, const int *A, const int *B, const int &n ) {
		int L; for( L = 1 ; L <= 2 * n - 2 ; L <<= 1 );
		rep( i, 0, L - 1 ) P[i] = Q[i] = 0;
		rep( i, 0, n - 1 ) P[i] = A[i], Q[i] = B[i];
		Basics :: DIF( P, L );
		Basics :: DIF( Q, L );
		rep( i, 0, L - 1 ) MulEq( P[i], Q[i] );
		Basics :: DIT( P, L );
		rep( i, 0, n - 1 ) ret[i] = P[i];
	}
}

namespace PolyInv {
	int P[MAXN], Q[MAXN], U[MAXN], V[MAXN];

	void Newton( const int &n ) {
		if( n == 1 ) {
			U[0] = Inv( P[0] );
			return ;
		}
		int h = ( n + 1 ) >> 1, L; Newton( h );
		for( L = 1 ; L <= n + h - 2 ; L <<= 1 );
		rep( i, 0, L - 1 ) Q[i] = V[i] = 0;
		rep( i, 0, n - 1 ) Q[i] = P[i];
		rep( i, 0, h - 1 ) V[i] = U[i];
		Basics :: DIF( Q, L );
		Basics :: DIF( V, L );
		rep( i, 0, L - 1 ) Q[i] = Mul( V[i], Sub( 2, Mul( V[i], Q[i] ) ) );
		Basics :: DIT( Q, L );
		rep( i, h, n - 1 ) U[i] = Q[i];
	}

	inline void PolyInv( int *ret, const int *A, const int &n ) {
		rep( i, 0, n - 1 ) P[i] = A[i];
		Newton( n );
		rep( i, 0, n - 1 ) ret[i] = U[i];
	}
}

namespace PolyLn {
	int P[MAXN], PInv[MAXN];

	inline void PolyLn( int *ret, const int *A, const int &n ) {
		int L; for( L = 1 ; L <= 2 * n - 3 ; L <<= 1 );
		rep( i, 0, L - 1 ) P[i] = PInv[i] = 0;
		rep( i, 0, n - 1 ) P[i] = A[i];
		PolyInv :: PolyInv( PInv, P, n );
		Basics :: PolyDer( P, n );
		Basics :: DIF( P, L );
		Basics :: DIF( PInv, L );
		rep( i, 0, L - 1 ) MulEq( P[i], PInv[i] );
		Basics :: DIT( P, L );
		Basics :: PolyInt( P, n );
		rep( i, 0, n - 1 ) ret[i] = P[i];
	}
}

inline void Init( const int &n ) {
	fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
	ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
	rep( i, 0, n ) {
		pw2[i][0] = 2;
		if( i <= 2 ) pw2[i][i] = 2;
		for( int j = 1 ; j < i && j <= 2 ; j ++ )
			pw2[i][j] = Mul( pw2[i - 1][j], pw2[i - 1][j - 1] );
		for( int j = i + 1 ; j <= 2 ; j ++ )
			pw2[i][j] = 1;
		pwInv[i][0] = inv2; 
		if( i <= 2 ) pwInv[i][i] = inv2;
		for( int j = 1 ; j < i && j <= 2 ; j ++ )
			pwInv[i][j] = Mul( pwInv[i - 1][j], pwInv[i - 1][j - 1] );
		for( int j = i + 1 ; j <= 2 ; j ++ )
			pwInv[i][j] = 1;
	}
}

int main() {
	Basics :: NTTInit();
	read( N );
	rep( i, 1, N ) {
		read( siz[i] );
		M = Max( M, siz[i] );
	}
	Init( M );
	rep( i, 0, M ) F[i] = Mul( ifac[i], pw2[i][2] );
	rep( i, 0, M ) G[i] = Mul( ifac[i], pwInv[i][2] );
	PolyMul :: PolyMul( G, G, G, M + 1 );
	rep( i, 0, M ) MulEq( G[i], pw2[i][2] );
	PolyLn :: PolyLn( G, G, M + 1 );
	PolyLn :: PolyLn( F, F, M + 1 );
	rep( i, 0, M ) MulEq( G[i], inv2 );
	rep( i, 0, M ) A[i] = Mul( ifac[i], pw2[i][2] );
	rep( i, 1, M ) B[i] = Mul( i, F[i] );
	rep( i, 2, M ) C[i] = G[i];
	rep( i, 2, M ) D[i] = Sub( F[i], G[i] );
	PolyMul :: PolyMul( B, A, B, M + 1 );
	PolyMul :: PolyMul( C, A, C, M + 1 );
	PolyMul :: PolyMul( D, A, D, M + 1 );
	int val1 = 1, val2 = 1, val3 = 1, val4 = 1;
	rep( i, 1, N ) {
		int a = Mul( fac[siz[i]], B[siz[i]] ),
			b = Mul( siz[i], pw2[siz[i] - 1][2] ),
			c = Mul( fac[siz[i]], C[siz[i]] ),
			d = Mul( fac[siz[i]], D[siz[i]] );
		MulEq( val1, a );
		MulEq( val2, Sub( a, b ) );
		MulEq( val3, Add( Mul( 2, c ), d ) );
		MulEq( val4, d );
	}
	write( Sub( Add( val1, Mul( inv2, Add( val3, val4 ) ) ), val2 ) ), putchar( '\n' );
	return 0;
}
posted @ 2022-06-22 15:55  crashed  阅读(57)  评论(0编辑  收藏  举报