• 「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;
    }
    
  • 相关阅读:
    VS2017 无法连接到Web服务器 IIS Express ,IIS Express可以启动,但是无法连接
    ADO.Net实体数据模型添加DB-First/Code First报错
    VS2017+EF6+MySQL8.0配置(.Net Framework 4.5)
    C#对象、List<>转DataTable
    ubuntu 16 安装django nginx uWSGI
    iOS真机测试could not find developer disk image
    iOS App 不支持http协议 App Transport Security has blocked a cleartext HTTP (http://)
    Objective-C Mojo和Django 对接
    第一课 ionic 日志输出
    ionic 使用sqlite
  • 原文地址:https://www.cnblogs.com/crashed/p/16400935.html
Copyright © 2020-2023  润新知