[JZOJ3303][BZOJ3456] 城市规划【多项式】【生成函数】【未完成】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83141974

Description

刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.
刚才说过, 阿狸的国家有n 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通.
为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.
好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出n 个点的简单(无重边无自环)无向连通图数目.
由于这个数字可能非常大, 你只需要输出方案数mod 1004535809(479 * 2 ^21 + 1)即可.
n<=130000

Solution

我们有两种思路
一是考虑DP
f [ i ] f[i] 为i个点的无向连通图个数, g [ i ] g[i] 为i个点的无向图个数
显然 g [ i ] = 2 n ( n 1 ) 2 g[i]=2^{n(n-1)\over 2} (每条边有或没有)

正难则反,我们可以考虑用总的减去不连通的
不妨枚举某一个点所在的联通块的大小(为了避免重复,我们当做枚举的点是标号最小的那个)
那么 f [ i ] = g [ i ] j = 1 i 1 f [ j ] ( i 1 j 1 ) g [ i j ] f[i]=g[i]-\sum\limits_{j=1}^{i-1}f[j]*{i-1\choose j-1}*g[i-j]
枚举1号点所在连通块大小,组合数计算其中点的情况,剩下i-j个点随便组成一个无向图
这一定唯一对应了一种不合法情况

直接转移复杂度是不能接受的,考虑优化
看到 f [ j ] g [ i j ] f[j]*g[i-j] 我们容易想到卷积
组合数拆开
f [ i ] = g [ i ] j = 1 i 1 f [ j ] ( i 1 ) ! ( j 1 ) ! ( i j ) ! g [ i j ] f[i]=g[i]-\sum\limits_{j=1}^{i-1}f[j]*{(i-1)!\over (j-1)!(i-j)!}*g[i-j]
分配一下,(i-1)除过来
f [ i ] ( i 1 ) ! = g [ i ] ( i 1 ) ! j = 1 i 1 f [ j ] ( j 1 ) ! g [ i j ] ( i j ) ! {f[i]\over (i-1)!}={g[i]\over (i-1)!}-\sum\limits_{j=1}^{i-1}{f[j]\over (j-1)!}*{g[i-j]\over (i-j)!}

此时可以考虑用CDQ分治
对于分治区间 [ l , r ] [l,r] ,递归计算 [ l , m i d ] [l,mid] ,然后用NTT快速计算出 [ l , m i d ] [l,mid] [ m i d + 1 , r ] [mid+1,r] 的贡献,然后递归右边即可

复杂度 O ( n log 2 n ) O(n\log^2 n)

然而这不是重点

如果我们移项,将左边的并入右边卷积式子,就有
g [ i ] ( i 1 ) ! = j = 1 i f [ j ] ( j 1 ) ! g [ i j ] ( i j ) ! {g[i]\over (i-1)!}=\sum\limits_{j=1}^{i}{f[j]\over (j-1)!}*{g[i-j]\over (i-j)!}

如果我们将上面三部分分别看成三个多项式 A ( x ) , F ( x ) , B ( x ) A(x),F(x),B(x)
A ( x ) = i &gt; 0 g [ i ] x i ( i 1 ) ! A(x)=\sum\limits_{i&gt;0}{g[i]*x^i\over (i-1)!}
B ( x ) = i 0 g [ i ] x i i ! B(x)=\sum\limits_{i\geq0}{g[i]*x^i\over i!}
F ( x ) = i &gt; 0 f [ i ] x i ( i 1 ) ! F(x)=\sum\limits_{i&gt;0}{f[i]*x^i\over (i-1)!}

容易看出 A ( x ) = B ( x ) F ( x ) A(x)=B(x)F(x)
由于 A ( x ) , B ( x ) , F ( x ) A(x),B(x),F(x) 都是无穷多项的多项式(形式幂级数),我们只需要 x n x^n 前面的项
用多项式求逆,可以得到 F ( x ) = A ( x ) B 1 ( x ) F(x)=A(x)B^{-1}(x)
这样在 O ( n log n ) O(n\log n) 时间内就得到答案了

看出来了吗? A ( x ) , B ( x ) , F ( x ) A(x),B(x),F(x) 实际上都是指数型生成函数

从生成函数的角度入手
f [ i ] f[i] 的EGF为 F ( x ) F&#x27;(x) , g [ i ] g[i] 的EGF为 G ( x ) G&#x27;(x)
那么 F ( x ) = i &gt; 0 f [ i ] x i i ! F&#x27;(x)=\sum\limits_{i&gt;0}{f[i]*x^i\over i!}
A ( x ) = i &gt; 0 g [ i ] x i i ! A&#x27;(x)=\sum\limits_{i&gt;0}{g[i]*x^i\over i!}

无向图实际上就是由若干个连通块拼接而成,因此这实际上就是带标号对象重复拼接的过程(若干个F’卷在一起)。
因此
A ( x ) = i &gt; 0 ( F ( x ) ) i i ! A&#x27;(x)=\sum\limits_{i&gt;0}{\left(F&#x27;(x)\right)^i\over i!} (因为连通块之间没有顺序之分,因此要除以i!)

也就是说 A ( x ) = e F ( x ) A&#x27;(x)=e^{F&#x27;(x)}
那么应用多项式求 ln \ln 就可以通过A’来求出F’了,进而求出f

复杂度同样是 O ( n log n ) O(n\log n)

Code

以下是多项式求逆的方法

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 130005
#define M 524288
#define LL long long
#define mo 1004535809
using namespace std;
int bit[M+1],n,wi[M+1],wg[M+1],cf[20],l2[M+1];
LL a[M+1],b[M+1],u1[M+1],u2[M+1],c[M+1],ny[M+1];
LL ksm(LL k,LL n)
{
	LL s=1;
	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
	return s;
}
void prp(int num)
{
	int L=l2[num];
	fo(i,0,num-1) bit[i]=(bit[i>>1]>>1)|((i&1)<<(L-1));
	fo(i,0,num) wi[i]=wg[i*(M/num)];
}
void NTT(LL *a,bool pd,int num)
{
	fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
	int lim=num>>1;
	LL v=0;
	for(int m=2,half=1;m<=num;half=m,m<<=1,lim>>=1)
	{
		fo(i,0,half-1)
		{
			LL w=(!pd)?wi[i*lim]:wi[num-i*lim];
			for(int j=i;j<num;j+=m)
			{
				v=w*a[j+half]%mo;
				a[j+half]=(a[j]-v+mo)%mo;
				a[j]=(a[j]+v)%mo;
			}
		}
	}
	if(pd) fo(i,0,num-1) a[i]=a[i]*ny[num]%mo;
}
void mul(const LL *a,const LL *b,LL *c,int L)
{
	int num=cf[L];
	prp(num);
	fo(i,0,num-1) u1[i]=a[i],c[i]=b[i];
	NTT(u1,0,num),NTT(c,0,num);
	fo(i,0,num-1) c[i]=c[i]*u1[i]%mo;
	NTT(c,1,num);
}
void make(const LL *a,LL *b,int L)
{
	b[0]=ksm(a[0],mo-2);
	for(int m=1,t=2,num=4;m<cf[L];m=t,t=num,num<<=1)
	{
		prp(num);
		fo(i,0,m-1) u1[i]=a[i],u2[i]=b[i];
		fo(i,m,t-1) u1[i]=a[i],u2[i]=0;
		fo(i,t,num-1) u1[i]=u2[i]=0;
		NTT(u1,0,num),NTT(u2,0,num);
		fo(i,0,num-1) u2[i]=u2[i]*u2[i]%mo*u1[i]%mo;
		NTT(u2,1,num);
		fo(i,0,t-1) b[i]=((LL)2*b[i]-u2[i]+mo)%mo;
		fo(i,t,num-1) b[i]=0;
	}
}
int main()
{
	cin>>n;
	cf[0]=1;
	fo(i,1,19) l2[cf[i]=cf[i-1]*2]=i;
	fod(i,M-1,2) if(!l2[i]) l2[i]=l2[i+1];
	ny[0]=ny[1]=1;
	fo(i,2,M) ny[i]=(-ny[mo%i]*(LL)(mo/i)%mo+mo)%mo;
	LL c1=ksm(3,1916);
	wg[0]=1;
	fo(i,1,M) wg[i]=wg[i-1]*c1%mo;
	LL s=1,t=1;
	b[0]=1;
	fo(i,1,n) 
	{
		s=s*ksm(i,mo-2)%mo;
		LL v=ksm(2,(LL)i*(LL)(i-1)/2)*s%mo;
		a[i]=v*(LL)i%mo;
		b[i]=v%mo;
		if(i!=n) t=t*(LL)i%mo;
	} 
	make(b,c,l2[n+1]);
	mul(a,c,b,l2[n+1]);
	printf("%lld",t*b[n]%mo);
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83141974