洛谷4841:城市规划

洛谷4841:城市规划

题意:

\(n\)个点的简单(无重边无自环)有标号无向连通图数目。

思路:

\(f(n)\)为点数为\(n\)的无向连通图的数量,\(g(n)\)为点数为\(n\)的无向图的数量,可以知道:

\[g(n)=2^{C_n^2} \]

可以这么理解,\(n\)个点中取两个点连边,一共有\(C_n^2\)种,每个边存在连或不连两种可能。

同时有:

\[g(n)=\sum_{i=1}^nC_{n-1}^{i-1}f(i)\times g(n-i) \]

这个可以这么理解,对于一号点,枚举它所在的连通块中有多少个点。

代入\(g(n)\)可得:

\[2^{C_n^2}=\sum_{i=1}^nC_{n-1}^{i-1}f(i)2^{C_{n-i}^2} \]

化简一下:

\[2^{C_n^2}=\sum_{i=1}^n\frac{(n-1)!f(i)2^{C_{n-i}^2}}{(i-1)!(n-i)!}\\\frac{2^{C_n^2}}{(n-1)!}=\sum_{i=1}^n\frac{f(i)}{(i-1)!}\times \frac{2^{C_{n-i}^2}}{(n-i)!} \]

\[F(x)=\sum_{n=1}^{+\infin}\frac{f(n)}{(n-1)!}x^n\\G(x)=\sum_{n=0}^{+\infin}\frac{2^{C_n^2}}{n!}x^n\\H(x)=\sum_{n=1}^{+\infin}\frac{2^{C_n^2}}{(n-1)!}x^n \]

那么有:

\[F\equiv H*G^{-1}(mod\ x^{n+1}) \]

NTT+多项式求逆求解。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 6e5+10;

const ll mod = 1004535809;
const int G = 3;
int n;

ll inv[maxn];   //逆元
ll fiv[maxn];   //逆元的阶乘
ll g1[maxn], g2[maxn], g3[maxn];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while(b)
    {
        if(b&1) res = (res*a)%mod;
        b >>= 1;
        a = (a*a)%mod;
    } return res%mod;
}

int limit, len, rev[maxn];
void NTT(ll *c, int op)
{
    for(int i = 1; i <= limit; i++)
        if(i < rev[i]) swap(c[i], c[rev[i]]);

    for(int mid = 1; mid < limit; mid <<= 1)
    {
        ll gn = qmi(G, (mod-1)/(mid<<1));
        if(op == -1) gn = qmi(gn, mod-2);
        for(int j = 0, R = mid<<1; j < limit; j += R)
        {
            ll g = 1;
            for(int k = 0; k < mid; k++, g = (g*gn)%mod)
            {
                ll x = c[j+k], y = g*c[j+k+mid]%mod;
                c[j+k] = (x+y)%mod;
                c[j+k+mid] = (x-y+mod)%mod;
            }
        }
    }
    if(op == 1) return;
    ll inv = qmi(limit, mod-2);
    for(int i = 0; i <= limit; i++) c[i] = c[i]*inv%mod;
}

ll tmp[maxn];
void get_inv(ll *a, ll *b, int deg)
{
	if(deg == 1)
	{
		b[0] = qmi(a[0], mod-2);
		return;
	}
	get_inv(a, b, (deg+1)>>1);

	len = 0, limit = 1;
	while(limit <= (deg+deg)) limit <<= 1, len++;

	for(int i = 1; i <= limit; i++)
		rev[i] = (rev[i>>1]>>1)|((i&1)<<(len-1));

	for(int i = 0; i < deg; i++) tmp[i] = a[i];
	for(int i = deg; i <= limit; i++) tmp[i] = 0;

	NTT(tmp, 1), NTT(b, 1);
	for(int i = 0; i <= limit; i++)
		b[i] = 1ll*(2-1ll*tmp[i]*b[i]%mod+mod)%mod*b[i]%mod;
	NTT(b, -1);
	for(int i = deg; i <= limit; i++) b[i] = 0;
}

int main()
{
    scanf("%d", &n); n += 1;
    fiv[0]=fiv[1]=inv[0]=inv[1]=1;
    for(int i = 2; i <= n; i++)
    {
        inv[i] = (ll(mod-mod/i)*inv[mod%i])%mod;
        fiv[i] = fiv[i-1]*inv[i]%mod;
    }

    g3[0] = 1;
    for(ll i = 1; i < n; i++)
    {
        ll tmp = qmi(2, (i*(i-1)/2)%(mod-1))%mod;//欧拉降幂
        g1[i] = tmp*fiv[i-1]%mod;
        g3[i] = tmp*fiv[i]%mod;
    }
    get_inv(g3, g2, n);

    NTT(g1, 1); NTT(g2, 1);

    for(int i = 0; i <= limit; i++) g1[i] = g1[i]*g2[i]%mod;

    NTT(g1, -1);

    ll ans = g1[n-1]%mod*qmi(fiv[n-2], mod-2)%mod;
    cout << ans << endl;
    return 0;
}

posted @ 2020-05-08 08:46  zhaoxiaoyun  阅读(195)  评论(0编辑  收藏  举报