點這裡看題目。
考慮如下遞推:
\(f_i\):\(i\)個點的無向有標號連通圖的個數。
\(g_i\):\(i\)個點的無向有標號圖的個數。
以下給出兩種計算方式。
不難看出乙個式子:
\[g_n=\sum_^n\binomf_ig_
\]這相當於列舉 1 所在的連通塊大小,然後構造出這個連通塊,剩下的點任意連。
然後感覺這個東西非常的 egf ,就嘗試拆一下:
\[\frac=\sum_^n\frac\cdot\frac}
\]由於負數的階乘是未定義的,我們就認為它是 0 ,那麼就可以簡化下標:
\[\frac=\sum_^n\frac\cdot\frac}
\]另外,由於任意乙個無向有標號圖一定是:
\[\begin
&g=(v,e')\\
&v=\\\
&e=\, e'\subseteq e
\end
\]那麼我們可以得到:
\[g_n=2^}
\]實際上就是看每條邊選不選。
帶入到式子中可以得到:
\[\frac}=\sum_^n\frac\cdot \frac 2}}
\]反手寫成生成函式:
\[\begin
h(x)&=\sum_^ \frac}x^i\\
f(x)&=\sum_^ \fracx^i\\
g(x)&=\sum_^ \frac}x^i
\end
\]於是有:
\[\begin
h(x)&=f(x)*g(x)\\
f(x)&=h(x)*g^(x)
\end
\]多項式求逆,結束。時間是\(o(n\log_2n)\)。
為了方便,我們先不考慮標號,記:
\[p_i=\frac,q_i=\frac
\]把它寫成生成函式的形式:
\[\begin
p(x)&=\sum_^ p_ix^i\\
q(x)&=\sum_^ q_ix^i
\end
\]然後可以發現:
\[\begin
q(x)=\sum_^ \frac
\end
\]其中\(k\)是在列舉連通塊的個數;除以\(k!\),是因為\((p(x))^k\)使得連通塊之間存在順序,然而這樣就算重複了,因此要除去。
發現等式右邊是經典的\(e^x\)的展開式,縮回去得到:
\[q(x)=e^
\]轉為\(p(x)=\ln q(x)\)計算即可。時間是\(o(n\log_2n)\)。
兩個方法都是正確的,但是結果看起來還不太一樣,我們進一步分析一下:
可以發現,多項式存在如下的關係:
\[h(x)=xg'(x),f(x)=xp'(x)
\]那麼就有:
\[\begin
f(x)&=\frac\\
x\cdot p'(x)&=x\cdot \frac\\
p'(x)&=\ln' g(x)\\
p(x)&=\ln g(x)
\end
\]所以說兩個方法的結果是統一的。只不過法一的**簡單一些而已
作者太懶了,只有法一的**
#include #include typedef long long ll;
const int mod = 1004535809, phi = mod - 1, g = 3;
const int maxn = 1e6 + 5;
templatevoid read( _t &x )
while( s >= '0' && s <= '9' )
x *= f;
}templatevoid write( _t x )
if( 9 < x )
putchar( x % 10 + '0' );
}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 )
return ret;
}int inv( const int a )
int fix( const int a )
void init( const int len )
void ntt( int *coe, const int len, const int t )
#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 = 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 ) }
namespace polymul
}int main()
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;
}
集訓隊互測2013 城市規劃
求n個點的帶標號連通簡單圖的個數。答案對1004535809取模。n 130000 根據套路,首先我們需要求出n個點的帶標號簡單圖的個數,設為g n 顯然我們有g n 2 n n 1 2 考慮設答案為f n 要怎麼求出?列舉1號點所在的聯通塊的大小,我們可以知道 g n i 1n f i ci 1 ...
P4841 集訓隊作業2013 城市規劃
設 f i 表示 i 個點的無向連通圖個數,g i 表示 i 個點的無向圖個數。列舉 1 所在連通塊的大小,有 g i sum limits ic f jg 化簡得 g i sum limits i fracf jg frac sum limits i frac frac 設 f i frac,g ...
P4841 集訓隊作業2013 城市規劃 題解
求 n 個點的有標號的無向連通圖數目。text 1 le n le1.3 times10 5 我們設 g i 表示有 i 個點的有標號無向圖數目,易得 g i 2 考慮列舉每兩個點之間是否連通。設 f i 表示有 i 個點的連通圖數目 即答案所求 則可得 g n sum n c f i g 考慮 1...