luogu P4841 [集训队作业2013]城市规划 FFT

FFT神仙题,强烈建议先自己推一推式子再看题解。

首先正着想比较难想,正难则反,所以我们先考虑一下全集。设(displaystyle g[n]=2^{C_n^2})(C为组合数),表示n个有标号的点随便连边的方案数,设(f[n])是n个有标号的点的无向连通图的方案数。

考虑(g)(f)之间的关系,因为每一个点都不一样,所以我们枚举1号点所在连通图的大小就可以得到

(displaystyle f[n]=g[n]-sum_{i=1}^{n-1}C_{n-1}^{i-1}f[i]g[n-i]),

后面的(sum_{i=1}^{n-1}C_{n-1}^{i-1}f[i]g[n-i])是枚举1号点所在联通块的大小,注意这里i不能等于n,因为等于n的时候它是一个连通图。

我们将等号两边转移一下得到

(displaystyle g[n]=sum_{i=1}^{n-1}C_{n-1}^{i-1}f[i]g[n-i]+f[n])

因为(i=n)时,(C_{n-1}^{n-1}f[n]g[0]=f[n]),所以

(displaystyle g[n]=sum_{i=1}^{n}C_{n-1}^{i-1}f[i]g[n-i])

我们知道(displaystyle C_n^m=frac{n!}{m!(n-m)!}),所以

(displaystyle g[n]=sum_{i=1}^{n}(n-1)!frac{f[i]}{(i-1)!}frac{g[n-i]}{(n-i)!})

因为((n-1)!)(i)无关。所以

(displaystyle g[n]=(n-1)!sum_{i=1}^{n}frac{f[i]}{(i-1)!}frac{g[n-i]}{(n-i)!})

(displaystyle frac{g[n]}{(n-1)!}=sum_{i=1}^{n}frac{f[i]}{(i-1)!}frac{g[n-i]}{(n-i)!})

我们设(displaystyle H[n]=frac{g[n]}{(n-1)!}) (displaystyle F[n]=frac{f[n]}{(n-1)!}) (displaystyle G[n]=frac{g[n]}{n!})

就能得到

(displaystyle H[n]=sum_{i=1}^nF[i]G[n-i])

又因为(i=0)(F[i]=0).所以

(displaystyle H[n]=sum_{i=0}^nF[i]G[n-i])

(displaystyle H=F*G)

(displaystyle F=frac{H}{G})

(displaystyle F=HG^{-1})

因为 (H)(G)已知的,所以我们多项式求个逆就OK啦。

时间复杂度(O(nlogn))

#include<bits/stdc++.h>
#define LL long long
using namespace std;
int n;
const int N = 500010, mod = 1004535809, YY = 3, YYinv = (mod + 1) / 3;
int r[N];
LL c[N], jc[N], inv[N], F[N], G[N], Ginv[N], H[N];
int read() 
{
	int x = 0; int f = 0; char c = getchar();
	while (!isdigit(c)) f |= c == '-', c = getchar();
	while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
	return f ? -x : x;
}
LL ksm(LL a, LL b, LL mod) 
{
	LL res = 1;
	for (; b; b >>= 1, a = a * a % mod)
		if (b & 1)res = res * a % mod;
	return res;
}
void NTT(LL *A, int lim, int opt) 
{
	for (int i = 0; i < lim; ++i)r[i] = (r[i >> 1] >> 1) | ((i & 1) ? (lim >> 1) : 0);
	for (int i = 0; i < lim; ++i)
		if (i < r[i])swap(A[i], A[r[i]]);
	int len;
	LL wn, w, x, y;
	for (int mid = 1; mid < lim; mid <<= 1) 
	{
		len = mid << 1;
		wn = ksm(opt == 1 ? YY : YYinv, (mod - 1) / len, mod);
		for (int j = 0; j < lim; j += len) 
		{
			w = 1;
			for (int k = j; k < j + mid; ++k, w = w * wn % mod) 
			{
				x = A[k]; y = A[k + mid] * w % mod;
				A[k] = (x + y) % mod;
				A[k + mid] = (x - y + mod) % mod;
			}
		}
	}
	if (opt == 1)return;
	LL ni = ksm(lim, mod - 2, mod);
	for (int i = 0; i < lim; ++i)A[i] = A[i] * ni % mod;
}
void MUL(LL*A, int n, LL *B, int m) 
{
	int lim = 1;
	while (lim <= (n + m))lim <<= 1;
	NTT(A, lim, 1); NTT(B, lim, 1);
	for (int i = 0; i < lim; ++i)A[i] = A[i] * B[i] % mod;
	NTT(A, lim, -1);
}
void INV(int siz, LL *A, LL *B) 
{
	if (siz == 1) 
	{
		B[0] = ksm(A[0], mod - 2, mod);
		return;
	}
	INV((siz + 1) >> 1, A, B);
	int lim = 1;
	while (lim < (siz << 1))lim <<= 1;
	for (int i = 0; i < siz; ++i)c[i] = A[i];
	for (int i = siz; i < lim; ++i)c[i] = 0;
	NTT(c, lim, 1); NTT(B, lim, 1);
	for (int i = 0; i < lim; ++i)B[i] = B[i] * (2 - c[i] * B[i] % mod + mod) % mod;
	NTT(B, lim, -1);
	for (int i = siz; i < lim; ++i)B[i] = 0;
}
void YYCH() 
{
	jc[0] = jc[1] = inv[0] = inv[1] = 1;
	for (int i = 1; i <= n; ++i)jc[i] = jc[i - 1] * i % mod;
	inv[n] = ksm(jc[n], mod - 2, mod);
	for (int i = n - 1; i >= 1; --i)inv[i] = inv[i + 1] * (i + 1) % mod;

	for (int i = 0; i <= n; ++i)G[i] = ksm(2, (LL)i * (i - 1) / 2, mod) * inv[i] % mod;
	for (int i = 1; i <= n; ++i)H[i] = ksm(2, (LL)i * (i - 1) / 2, mod) * inv[i - 1] % mod;
}
signed main() 
{
	cin >> n;
	YYCH();
	INV(n + 1, G, Ginv);
	MUL(H, n, Ginv, n);
	cout << H[n]*jc[n - 1] % mod;
	return 0;
}
原文地址:https://www.cnblogs.com/wljss/p/12029820.html