zoukankan      html  css  js  c++  java
  • 题解 边双连通图计数

    题目传送门

    题目大意

    给定一个(n),求出点数为(n)的边双连通图的个数。

    思路

    其实思路跟点双连通分量计数差不多的。

    我们设(F(x))为有标号无向图的指数级生成函数,(G(x))为有标号无向连通图的指数型生成函数。可以得到:

    [F(x)=sum_{i=1}^{infty} frac{2^{inom{i}{2}}}{i!}x^i ]

    [F(x)=e^{G(x)} ightarrow G(x)=ln F(z) ]

    接着我们设(D(x))为有根无向连通图的指数型生成函数,(B(x))为有根无向边双连通分量的指数型生成函数,我们可以得到:

    [D(x)=sum_{i=1} frac{b_ie^{iD(x)}}{i!}x^i ]

    性感证明就是我们根所在的边双联通分量大小如果为(i),那么就相当于把连通图通过一条边挂在(i)个点上面,而不是跟点双联通计数一样选一个点为连通图的根,就是(e^{iD(x)}),而边双联通分量又有(b_i)种方法。

    于是,从上面的式子我们可以推得:

    [D(x)=B(xe^{D(x)}) ]

    我们如果设(F(x)=xe^{D(x)}),则(D(x)=B(F(x))),两边同时做复合逆,可以得到(B(x)=D(F^{-1}(x)))。于是,这里我们就可以使用拓展拉格朗日反演了:

    [[x^n]B(x)=[x^n]D(F^{-1}(x)) ]

    [=frac{1}{n}[x^{-1}]D^{'}(x)F(x)^{-n} ]

    [=frac{1}{n}[x^{n-1}]D^{'}(x)(frac{x}{F(x)})^n ]

    [=frac{1}{n}[x^{n-1}]D^{'}(x)(frac{x}{xe^{D(x)}})^n ]

    [=frac{1}{n}[x^{n-1}]D^{'}(x)e^{-nD(x)} ]

    于是,我们就可以在(Theta(nlog n))的时间复杂度内解决这个问题。但是我常数似乎很大。。。

    ( ext {Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define mod 998244353
    #define Gii 332748118
    #define ll long long
    #define MAXN 300005
    #define Gi 3
    
    int quick_pow (int a,int b){
    	int res = 1;for (;b;b >>= 1,a = 1ll * a * a % mod) if (b & 1) res = 1ll * res * a % mod;
    	return res;
    }
    
    int limit,l,r[MAXN];
    
    void NTT (int *a,int type){
    	for (Int i = 0;i < limit;++ i) if (i < r[i]) swap (a[i],a[r[i]]);
    	for (Int mid = 1;mid < limit;mid <<= 1){
    		int Wn = quick_pow (type == 1 ? Gi : Gii,(mod - 1) / (mid << 1));
    		for (Int R = mid << 1,j = 0;j < limit;j += R){
    			for (Int k = 0,w = 1;k < mid;++ k,w = 1ll * w * Wn % mod){
    				int x = a[j + k],y = 1ll * w * a[j + k + mid] % mod;
    				a[j + k] = (x + y) % mod,a[j + k + mid] = (x + mod - y) % mod;
    			}
    		}
    	} 
    	if (type == 1) return ;
    	int Inv = quick_pow (limit,mod - 2);
    	for (Int i = 0;i < limit;++ i) a[i] = 1ll * a[i] * Inv % mod;
    }
    
    int c[MAXN];
    
    void Solve (int len,int *a,int *b){
    	if (len == 1) return b[0] = quick_pow (a[0],mod - 2),void ();
    	Solve ((len + 1) >> 1,a,b);
    	limit = 1,l = 0;
    	while (limit < (len << 1)) limit <<= 1,l ++;
    	for (Int i = 0;i < limit;++ i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    	for (Int i = 0;i < len;++ i) c[i] = a[i];
    	for (Int i = len;i < limit;++ i) c[i] = 0;
    	NTT (c,1);NTT (b,1);
    	for (Int i = 0;i < limit;++ i) b[i] = 1ll * b[i] * (2 + mod - 1ll * c[i] * b[i] % mod) % mod;
    	NTT (b,-1);
    	for (Int i = len;i < limit;++ i) b[i] = 0;
    }
    
    void deravitive (int *a,int n){
    	for (Int i = 1;i <= n;++ i) a[i - 1] = 1ll * a[i] * i % mod;
    	a[n] = 0;
    }
    
    void inter (int *a,int n){
    	for (Int i = n;i >= 1;-- i) a[i] = 1ll * a[i - 1] * quick_pow (i,mod - 2) % mod;
    	a[0] = 0;
    }
    
    int b[MAXN];
    
    void Ln (int *a,int n){
    	memset (b,0,sizeof (b));
    	Solve (n,a,b);deravitive (a,n);
    	while (limit <= n) limit <<= 1,l ++;
    	for (Int i = 0;i < limit;++ i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    	NTT (a,1),NTT (b,1);
    	for (Int i = 0;i < limit;++ i) a[i] = 1ll * a[i] * b[i] % mod;
    	NTT (a,-1),inter (a,n);
    	for (Int i = n + 1;i < limit;++ i) a[i] = 0;
    }
    
    int F0[MAXN];
    
    void Exp (int *a,int *B,int n)
    {
    	if (n == 1) return B[0] = 1,void ();
    	Exp (a,B,(n + 1) >> 1);
    	for (Int i = 0;i < limit;++ i) F0[i] = B[i];
    	Ln (F0,n);
    	F0[0] = (a[0] + 1 + mod - F0[0]) % mod;
    	for (Int i = 1;i < n;++ i) F0[i] = (a[i] + mod - F0[i]) % mod;
    	NTT (F0,1);NTT (B,1);	
    	for (Int i = 0;i < limit;++ i) B[i] = 1ll * F0[i] * B[i] % mod;
    	NTT (B,-1);
    	for (Int i = n;i < limit;++ i) B[i] = 0;
    }
    
    int read ()
    {
    	int x = 0;char c = getchar();int f = 1;
    	while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}
    	while (c >= '0' && c <= '9'){x = (x << 3) + (x << 1) + c - '0';c = getchar();}
    	return x * f;
    }
    
    void write (int x)
    {
    	if (x < 0){x = -x;putchar ('-');}
    	if (x > 9) write (x / 10);
    	putchar (x % 10 + '0');
    }
    
    int fac[MAXN],caf[MAXN],lim = 140000;
    
    void init (){
    	fac[0] = 1;for (Int i = 1;i <= lim;++ i) fac[i] = 1ll * fac[i - 1] * i % mod;
    	caf[lim] = quick_pow (fac[lim],mod - 2);for (Int i = lim;i;-- i) caf[i - 1] = 1ll * caf[i] * i % mod;
    } 
    
    int H[MAXN],H_[MAXN],G[MAXN],FG[MAXN],SG[MAXN];
    
    void makerev (int len){
    	limit = 1,l = 0;
    	while (limit < len) limit <<= 1,l ++;
    	for (Int i = 0;i < limit;++ i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << l - 1);
    }
    
    void prepare (){
    	int len = 1 << 17;makerev (len);
    	for (Int i = 0;i < len;++ i) H[i] = 1ll * quick_pow (2,1ll * i * (i - 1) / 2 % (mod - 1)) * caf[i] % mod;
    	Ln (H,len - 1);
    	for (Int i = 0;i < len;++ i) H[i] = H_[i] = 1ll * H[i] * i % mod;
    	deravitive (H_,len - 1),makerev (len << 1),NTT (H_,1);
    }
    
    void work (int n){
    	int len = 1 << 17;
    	memset (SG,0,sizeof (SG)),memset (F0,0,sizeof (F0));
    	for (Int i = 0;i < len;++ i) G[i] = 1ll * H[i] * (mod - n) % mod;
    	Exp (G,SG,len),makerev (len << 1),NTT (SG,1);
    	for (Int i = 0;i < len << 1;++ i) SG[i] = 1ll * SG[i] * H_[i] % mod;
    	NTT (SG,-1);
    	write (1ll * SG[n - 1] * quick_pow (n,mod - 2) % mod * fac[n - 1] % mod),putchar ('
    ');
    }
    
    signed main(){
    	init (),prepare ();
    	for (Int i = 1;i <= 5;++ i) work (read ());
    	return 0;
    }
    
  • 相关阅读:
    Java 类和Static关键字
    算法与数据结构实验题 6.4 Summary
    Django-----vue结合上传图片
    Django----模板继承&过滤器
    Django-----多对多示例查询
    Django-----验证码
    Django-----序列化--jwt
    Django-----文件配置
    Django-----删除--批量删除
    Django-----图文混排
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13285767.html
Copyright © 2011-2022 走看看