• 【洛谷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
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    为图片指定区域添加链接
    数值取值范围问题
    【leetcode】柱状图中最大的矩形(第二遍)
    【leetcode 33】搜索旋转排序数组(第二遍)
    【Educational Codeforces Round 81 (Rated for Div. 2) C】Obtain The String
    【Educational Codeforces Round 81 (Rated for Div. 2) B】Infinite Prefixes
    【Educational Codeforces Round 81 (Rated for Div. 2) A】Display The Number
    【Codeforces 716B】Complete the Word
    一个简陋的留言板
    HTML,CSS,JavaScript,AJAX,JSP,Servlet,JDBC,Structs,Spring,Hibernate,Xml等概念
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu7364.html
Copyright © 2020-2023  润新知