[集训队作业]城市规划

题目

点这里看题目。

分析

考虑如下递推:

\(f_i\)\(i\)个点的无向有标号连通图的个数。

\(g_i\)\(i\)个点的无向有标号图的个数。

以下给出两种计算方式。

法一

不难看出一个式子:

\[g_n=\sum_{i=1}^n\binom{n-1}{i-1}f_ig_{n-i} \]

这相当于枚举 1 所在的连通块大小,然后构造出这个连通块,剩下的点任意连。

然后感觉这个东西非常的 EGF ,就尝试拆一下:

\[\frac{g_n}{(n-1)!}=\sum_{i=1}^n\frac{f_i}{(i-1)!}\cdot\frac{g_{n-i}}{(n-i)!} \]

由于负数的阶乘是未定义的,我们就认为它是 0 ,那么就可以简化下标:

\[\frac{g_n}{(n-1)!}=\sum_{i=0}^n\frac{f_i}{(i-1)!}\cdot\frac{g_{n-i}}{(n-i)!} \]

另外,由于任意一个无向有标号图一定是:

\[\begin{aligned} &G=(V,E')\\ &V=\{1,2,3,...,n\}\\ &E=\{(u,v)|u,v\in V\}, E'\subseteq E \end{aligned} \]

那么我们可以得到:

\[g_n=2^{\binom{n}{2}} \]

实际上就是看每条边选不选。

带入到式子中可以得到:

\[\frac{2^{\binom n 2}}{(n-1)!}=\sum_{i=0}^n\frac{f_i}{(i-1)!}\cdot \frac{2^{\binom {n - i} 2}}{(n-i)!} \]

反手写成生成函数:

\[\begin{aligned} H(x)&=\sum_{i=1}^{+\infty} \frac{2^{\binom i 2}}{(i-1)!}x^i\\ F(x)&=\sum_{i=1}^{+\infty} \frac{f_i}{(i-1)!}x^i\\ G(x)&=\sum_{i=0}^{+\infty} \frac{2^{\binom i 2}}{i!}x^i \end{aligned} \]

于是有:

\[\begin{aligned} H(x)&=F(x)*G(x)\\ F(x)&=H(x)*G^{-1}(x) \end{aligned} \]

多项式求逆,结束。时间是\(O(n\log_2n)\)

法二

为了方便,我们先不考虑标号,记:

\[p_i=\frac{f_i}{i!},q_i=\frac{g_i}{i!} \]

把它写成生成函数的形式:

\[\begin{aligned} P(x)&=\sum_{i=0}^{+\infty} p_ix^i\\ Q(x)&=\sum_{i=0}^{+\infty} q_ix^i \end{aligned} \]

然后可以发现:

\[\begin{aligned} Q(x)=\sum_{k=0}^{+\infty} \frac{(P(x))^k}{k!} \end{aligned} \]

其中\(k\)是在枚举连通块的个数;除以\(k!\),是因为\((P(x))^k\)使得连通块之间存在顺序,然而这样就算重复了,因此要除去。

发现等式右边是经典的\(e^x\)的展开式,缩回去得到:

\[Q(x)=e^{P(x)} \]

转为\(P(x)=\ln Q(x)\)计算即可。时间是\(O(n\log_2n)\)

题外话

两个方法都是正确的,但是结果看起来还不太一样,我们进一步分析一下:

可以发现,多项式存在如下的关系:

\[H(x)=xG'(x),F(x)=xP'(x) \]

那么就有:

\[\begin{aligned} F(x)&=\frac{H(x)}{G(x)}\\ x\cdot P'(x)&=x\cdot \frac{G'(x)}{G(x)}\\ P'(x)&=\ln' G(x)\\ P(x)&=\ln G(x) \end{aligned} \]

所以说两个方法的结果是统一的。只不过法一的代码简单一些而已

代码

作者太懒了,只有法一的代码

#include <cmath>
#include <cstdio>

typedef long long LL;

const int mod = 1004535809, phi = mod - 1, g = 3;
const int MAXN = 1e6 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && 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 ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
void swapp( _T &x, _T &y )
{
	_T t = x; x = y, y = t;
}

int rev[MAXN], wp[MAXN], wpinv[MAXN];
int Q[MAXN], G[MAXN], H[MAXN];
int fac[MAXN], finv[MAXN];
int N;

int qkpow( int base, int indx )
{
	int ret = 1;
	while( indx )
	{
		if( indx & 1 ) ret = 1ll * ret * base % mod;
		base = 1ll * base * base % mod, indx >>= 1;
	}
	return ret;
}

int inv( const int a ) { return qkpow( a, phi - 1 ); }
int fix( const int a ) { return ( a % mod + mod ) % mod; }

void init( const int len )
{
	int lg2 = log2( len );
	for( int i = 0 ; i < len ; i ++ )
		rev[i] = wp[i] = wpinv[i] = 0;
	for( int i = 0 ; i < len ; i ++ )
		for( int j = 0 ; j < lg2 ; j ++ )
			rev[i] |= ( i >> j & 1 ) << ( lg2 - j - 1 );
	wp[0] = 1, wp[1] = qkpow( g, phi / len );
	wpinv[0] = 1, wpinv[1] = qkpow( g, phi - phi / len );
	for( int i = 2 ; i < len ; i ++ )
		wp[i] = 1ll * wp[i - 1] * wp[1] % mod,
		wpinv[i] = 1ll * wpinv[i - 1] * wpinv[1] % mod;
}

void NTT( int *coe, const int len, const int t )
{
	#define p ( s >> 1 )
	for( int i = 0 ; i < len ; i ++ )
		if( rev[i] < i )
			swapp( coe[i], coe[rev[i]] );
	int w, wo, we;
	for( int s = 2 ; s <= len ; s <<= 1 )
		for( int i = 0 ; i < len ; i += s )
			for( int j = 0 ; j < s >> 1 ; j ++ )
			{
				w = t > 0 ? wp[len / s * j] : wpinv[len / s * j];
				we = coe[i + j], wo = 1ll * coe[i + j + p] * w % mod;
				coe[i + j] = ( we + wo ) % mod, coe[i + j + p] = ( we - wo + mod ) % mod;
			}
	#undef p
	if( t > 0 ) return; int inver = inv( len );
	for( int i = 0 ; i < len ; i ++ ) coe[i] = 1ll * coe[i] * inver % mod;
}

namespace PolyInv
{
	int P[MAXN], F0[MAXN], F[MAXN], B[MAXN];
	
	void PolyInv( const int n )
	{
		if( n == 1 ) { F[0] = inv( P[0] ); return; }
		int p = n + 1 >> 1, len = 1;
		PolyInv( p );
		while( len < n << 1 ) len <<= 1; init( len );
		for( int i = 0 ; i < n ; i ++ ) B[i] = P[i];
		for( int i = 0 ; i < len ; i ++ ) F0[i] = F[i], F[i] = 0;
		NTT( B, len, 1 ), NTT( F0, len, 1 );
		for( int i = 0 ; i < len ; i ++ ) F[i] = 1ll * F0[i] * fix( 2 - 1ll * B[i] * F0[i] % mod ) % mod;
		NTT( F, len, -1 );
		for( int i = n ; i < len ; i ++ ) F[i] = 0;
	}
	
	void PolyInv( int *ret, int *A, const int n )
	{
		for( int i = 0 ; i < n << 2 ; i ++ ) P[i] = F0[i] = F[i] = B[i] = 0;
		for( int i = 0 ; i < n ; i ++ ) P[i] = A[i];
		PolyInv( n );
		for( int i = 0 ; i < n ; i ++ ) ret[i] = F[i];
	}
}

namespace PolyMul
{
	int A[MAXN], B[MAXN];
	
	void PolyMul( int *ret, int *a, const int La, int *b, const int Lb )
	{
		int len = 1;
		while( len < La + Lb ) len <<= 1;
		init( len );
		
		for( int i = 0 ; i < len ; i ++ ) ret[i] = A[i] = B[i] = 0;
		for( int i = 0 ; i < La ; i ++ ) A[i] = a[i];
		for( int i = 0 ; i < Lb ; i ++ ) B[i] = b[i];
		NTT( A, len, 1 ), NTT( B, len, 1 );
		for( int i = 0 ; i < len ; i ++ ) ret[i] = 1ll * A[i] * B[i] % mod;
		NTT( ret, len, -1 );
		for( int i = La + Lb ; i < len ; i ++ ) ret[i] = 0;
	}
}

int main()
{
	read( N );
	if( N == 0 ) return puts( "0" ), 0;
	fac[0] = 1, finv[1] = 1;
	for( int i = 2 ; i <= N ; i ++ ) finv[i] = 1ll * ( mod - mod / i ) * finv[mod % i] % mod;
	finv[0] = 1;
	for( int i = 1 ; i <= N ; i ++ ) fac[i] = 1ll * fac[i - 1] * i % mod, finv[i] = 1ll * finv[i - 1] * finv[i] % mod;
	N ++, G[0] = 1;
	for( int i = 1 ; i < N ; i ++ )
	{
		int tmp = qkpow( 2, 1ll * i * ( i - 1 ) / 2 % phi );
		G[i] = 1ll * tmp * finv[i] % mod, H[i] = 1ll * tmp * finv[i - 1] % mod;
	}
	PolyInv :: PolyInv( G, G, N );
	PolyMul :: PolyMul( Q, G, N, H, N );
	write( 1ll * Q[N - 1] * fac[N - 2] % mod ), putchar( '\n' );
	return 0;
}
posted @ 2020-07-11 17:44  crashed  阅读(182)  评论(0编辑  收藏  举报