【洛谷7364】有标号二分图计数(多项式开根)

点此看题面

  • 对于所有(n=1sim 10^5),求(n)个点的有标号二分图个数。

二分图染色方案数

我们发现直接计算二分图个数实际上并不好做。

因此,我们要引入一个与二分图相关同时能列出计算式的东西:二分图染色方案数

对于这个东西,我们只需要枚举有多少个点被染成黑色,多少个点被染成白色,只有异色点之间可以随意连边,即:

[f_n=sum_{i=0}^nC_n^i2^{i imes (n-i)} ]

其中(i imes (n-i))也可以写作(C_n^2-C_i^2-C_{n-i}^2)

也就是说:

[f_n=sum_{i=0}^nfrac{n!}{i!(n-i)!} imesfrac{2^{C_n^2}}{2^{C_i^2} imes 2^{C_{n-i}^2}}\ frac{f_n}{n!}=2^{C_n^2}sum_{i=0}^nfrac{1}{2^{C_i^2} imes i!} imes frac1{2^{C_{n-i}^2} imes (n-i)!} ]

后面显然是一个卷积的形式,只要列出一个多项式(p(x)=sum_{i=0}^nfrac1{2^{C_i^2} imes i!}x^i)自己卷自己就好了。

多项式开根

然后考虑二分图染色方案数和二分图个数的关系,这里我们分别考虑它们跟连通二分图个数的关系。

(F(x))表示二分图染色方案数的(EGF)(G(x))表示二分图个数的(EGF)(H(x))表示连通二分图个数的(EGF)

众所周知(G(x)=e^{H(x)})。又由于一个连通二分图有且仅有两种染色方案,所以(F(x)=e^{2H(x)})

也就是说,(G(x)=sqrt{F(x)}),直接多项式开根就完事了。

代码:(O(nlogn))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 998244353
#define I2 499122177
using namespace std;
int f[N+5],g[N+5],p[N+5],Fac[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
namespace Poly
{
	#define Init(n) P=1,L=0;W(P<=2*(n)) P<<=1,++L;
		for(RI i=0;i^P;++i) R[i]=((R[i>>1]>>1)|((i&1)<<L-1)),A[i]=B[i]=0;
	int PR=3,P,L,R[N<<2],A[N<<2],B[N<<2],p[N<<2];
	I void NTT(int* s,CI op)
	{
		RI i,j,k,x,y,U,S;for(i=0;i^P;++i) i<R[i]&&(swap(s[i],s[R[i]]),0);
		for(i=1;i^P;i<<=1) for(U=QP(QP(PR,op),(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,
			k=0;k^i;++k,S=1LL*S*U%X) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
	}
	I void Mul(CI n,int* a,int* b,int* c)//多项式乘法
	{
		Init(n);RI i;for(i=0;i<=n;++i) A[i]=a[i],B[i]=b[i];
		for(NTT(A,1),NTT(B,1),i=0;i^P;++i) A[i]=1LL*A[i]*B[i]%X;
		RI t=QP(P,X-2);for(NTT(A,X-2),i=0;i<=n;++i) c[i]=1LL*A[i]*t%X;
	}
	I void Inv(CI n,int* a,int* b)//多项式求逆
	{
		if(!n) return (void)(b[0]=QP(a[0],X-2));Inv(n>>1,a,b);
		Init(n);RI i;for(i=0;i<=n;++i) A[i]=a[i],B[i]=b[i];
		for(NTT(A,1),NTT(B,1),i=0;i^P;++i) A[i]=(2*B[i]-1LL*A[i]*B[i]%X*B[i]%X+X)%X;
		RI t=QP(P,X-2);for(NTT(A,X-2),i=0;i<=n;++i) b[i]=1LL*A[i]*t%X;
	}
	I void Sqrt(CI n,int* a,int* b)//多项式开根
	{
		if(!n) return (void)(b[0]=1);Sqrt(n>>1,a,b);
		Init(n);RI i;for(i=0;i^P;++i) p[i]=0;Inv(n,b,p);
		for(i=0;i<=n;++i) A[i]=a[i],B[i]=b[i];for(;i^P;++i) A[i]=B[i]=0;
		for(NTT(A,1),NTT(B,1),NTT(p,1),i=0;i^P;++i) A[i]=(A[i]+1LL*B[i]*B[i])%X*p[i]%X*I2%X;
		RI t=QP(P,X-2);for(NTT(A,X-2),i=0;i<=n;++i) b[i]=1LL*A[i]*t%X;
	}
}
int main()
{
	RI i;for(Fac[0]=i=1;i<=N;++i) Fac[i]=1LL*Fac[i-1]*i%X;
	for(i=0;i<=N;++i) p[i]=QP(1LL*QP(2,1LL*i*(i-1)/2%(X-1))*Fac[i]%X,X-2);
	for(Poly::Mul(N,p,p,f),i=0;i<=N;++i) f[i]=1LL*f[i]*QP(2,1LL*i*(i-1)/2%(X-1))%X;//求出F(x)
	for(Poly::Sqrt(N,f,g),i=1;i<=N;++i) printf("%d
",1LL*g[i]*Fac[i]%X);return 0;//多项式开根得到G(x),EGF乘上i!化为OGF
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu7364.html