洛谷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;
}