把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

二分图染色方案数

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

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

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

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

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

也就是说:

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

后面显然是一个卷积的形式,只要列出一个多项式\(p(x)=\sum_{i=0}^n\frac1{2^{C_i^2}\times 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\n",1LL*g[i]*Fac[i]%X);return 0;//多项式开根得到G(x),EGF乘上i!化为OGF
}
posted @ 2021-05-12 16:35  TheLostWeak  阅读(171)  评论(0)    收藏  举报