1. 程式人生 > >【bzoj3456】城市規劃 容斥原理+NTT+多項式求逆

【bzoj3456】城市規劃 容斥原理+NTT+多項式求逆

方案 所在 scanf 整理 輸入 eof 輸出 std define

題目描述

求出n個點的簡單(無重邊無自環)無向連通圖數目mod 1004535809(479 * 2 ^ 21 + 1).

輸入

僅一行一個整數n(<=130000)

輸出

僅一行一個整數, 為方案數 mod 1004535809.

樣例輸入

3

樣例輸出

4


題解

容斥原理+NTT+多項式求逆

設 $f_i$ 表示 $i$ 個點的簡單無向連通圖的數目,$g_i$ 表示 $i$ 個點的簡單無向圖的數目。

根據定義得 $g_i=2^{\frac{n(n-1}2}$ 。

對於 $f_i$ ,考慮容斥,用 $g_i$ 減去不連通的方案數。枚舉不連通圖中1號點所在連通塊大小 $j$ ,則有:

$f_i=g_i-\sum\limits_{j=1}^{i-1}C_{i-1}^{j-1}f_jg_{i-j}$

將組合數展開,得:

$f_i=g_i-\sum\limits_{j=1}^{i-1}\frac{(i-1)!}{(j-1)!(i-j)!}f_jg_{i-j}$

兩邊同時除以 $(i-1)!$ ,整理得:

$\frac{f_i}{(i-1)!}=\sum\limits_{j=1}^{i-1}\frac{f_j}{(j-1)!}\frac{g_{i-j}}{(i-j)!}$

設:

$F(x)=\sum\limits_{i=1}^{\infty}\frac{f_i}{(i-1)!}$

$G(x)=\sum\limits_{i=1}^{\infty}\frac{g_i}{(i-1)!}$

$H(x)=\sum\limits_{i=1}^{\infty}\frac{g_i}{i!}$

註意到 $F(x)$ 、$H(x)$ 的常數項均為0,因此後面的那個式子相當於 $\sum\limits_{j=0}^iF(x)[j]H(x)[i-j]$ ,是一個卷積的形式。

因此有:

$F(x)=G(x)-F(x)\times H(x)$

化簡得:

$F(x)=\frac{G(x)}{H(x)+1}$

剩下的就好辦了,根據定義求出 $G(x)$ 和 $H(x)+1$ ,使用多項式求逆求出 $H(x)+1$ 的逆,再與 $G(x)$ 求乘法即可得到 $F(x)$ 。

最後的答案就是 $F(x)[n]\times(n-1)!$ 。

時間復雜度 $O(n\log n)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 262200
#define mod 1004535809
using namespace std;
typedef long long ll;
ll A[N] , B[N] , C[N] , t[N] , fac[N >> 1];
inline ll pow(ll x , ll y)
{
    ll ans = 1;
    while(y)
    {
        if(y & 1) ans = ans * x % mod;
        x = x * x % mod , y >>= 1;
    }
    return ans;
}
void ntt(ll *a , int n , int flag)
{
    int i , j , k;
    for(i = k = 0 ; i < n ; i ++ )
    {
        if(i > k) swap(a[i] , a[k]);
        for(j = n >> 1 ; (k ^= j) < j ; j >>= 1);
    }
    for(k = 2 ; k <= n ; k <<= 1)
    {
        ll wn = pow(3 , (mod - 1) / k);
        if(flag == -1) wn = pow(wn , mod - 2);
        for(i = 0 ; i < n ; i += k)
        {
            ll w = 1 , t;
            for(j = i ; j < i + (k >> 1) ; j ++ , w = w * wn % mod)
                t = w * a[j + (k >> 1)] % mod , a[j + (k >> 1)] = (a[j] - t + mod) % mod , a[j] = (a[j] + t) % mod;
        }
    }
    if(flag == -1)
    {
        k = pow(n , mod - 2);
        for(i = 0 ; i < n ; i ++ ) a[i] = a[i] * k % mod;
    }
}
void inv(ll *a , ll *b , int n)
{
    if(n == 1)
    {
        b[0] = 1;
        return;
    }
    int i;
    inv(a , b , n >> 1);
    memcpy(t , a , sizeof(ll) * n);
    ntt(t , n << 1 , 1);
    ntt(b , n << 1 , 1);
    for(i = 0 ; i < n << 1 ; i ++ ) b[i] = b[i] * (2 - t[i] * b[i] % mod + mod) % mod;
    ntt(b , n << 1 , -1);
    memset(b + n , 0 , sizeof(ll) * n);
}
int main()
{
    int n , i , len = 1;
    scanf("%d" , &n);
    fac[0] = A[0] = 1;
    for(i = 1 ; i <= n ; i ++ )
    {
        fac[i] = fac[i - 1] * i % mod;
        A[i] = pow(2 , (ll)i * (i - 1) / 2) * pow(fac[i] , mod - 2) % mod;
        B[i] = pow(2 , (ll)i * (i - 1) / 2) * pow(fac[i - 1] , mod - 2) % mod;
    }
    while(len <= n) len <<= 1;
    inv(A , C , len);
    ntt(B , len , 1);
    ntt(C , len , 1);
    for(i = 0 ; i < len ; i ++ ) B[i] = B[i] * C[i] % mod;
    ntt(B , len , -1);
    printf("%lld\n" , B[n] * fac[n - 1] % mod);
    return 0;
}

【bzoj3456】城市規劃 容斥原理+NTT+多項式求逆