zoukankan      html  css  js  c++  java
  • UOJ498 新年的追逐战

    新年的追逐战

    杰瑞正在被汤姆追捕!

    为了摆脱追捕,杰瑞需要知道有地图有多少个老鼠洞可以隐藏!

    但两只鞋太太的家实在太大了,为了简单地说明,定义两个简单无向图 (G_{1} =( V_{1} ,E_{1}) ,G_{2} =( V_{2} ,E_{2})) 的乘积为一个新的图 (G_{1} imes G_{2} =left( V^{*} ,E^{*} ight))

    其中新的点集 (V^{*}) 为:

    [V^{*} = left{{(a,b)| a in V_{1}, b in V_{2}} ight} ]

    其中新的边集 (E^{*}) 为:

    [E^{*} =left{left(( u_{1} ,v_{1}) ,( u_{2} ,v_{2}) ight) mid ( u_{1} ,u_{2}) in E_{1}, ( v_{1} ,v_{2}) in E_{2} ight} ]

    对于正整数 (n),以及给定的图 (G_{1} ,G_{2} ,dotsc ,G_{n}),两只鞋太太的家可以表示成

    [H = (((G_1 imes G_2) imes G_3) imes cdots) imes G_n ]

    为了方便逃亡(戏弄汤姆),对于同一个连通块,杰瑞事先打通了所有的老鼠洞,你只需要计算一遍。

    为了方便逃亡(戏弄汤姆),对于同一个任何一个连通块,都有老鼠洞。

    也就是说,你需要求的是 (H) 的连通块数量。但是杰瑞忘记了每个 (G_k) 的具体细节,所以我们现在假设每个 (G_k) 中任意两点间都有 (frac12) 的概率连边,求 (H) 的连通块的期望。显然 (G_k) 的全体取法共有 ({large {2^{inom{m_1}2 + inom{m_2}2 + cdots + inom{m_n}2}}}) 种。

    方便起见,你只需要输出答案乘以 ({large {2^{inom{m_1}2 + inom{m_2}2 + cdots + inom{m_n}2}}}),对 (998244353) 取模即可。

    对于所有测试数据,满足 (1le n, m_kle 10^5)

    题解

    https://www.cnblogs.com/mathematician/p/13161880.html

    我们研究(G_1 imes G_2)的连通块的性质。不妨设(G_1, G_2)连通,且都不是孤立点。(若不连通,可以拆成若干连通块。若(G_1)是孤立点,则答案就是(lvert V(G_2) vert)

    考虑连通图的乘积(G_1 imes G_2)的两个点((u_1, u_2), (v_1, v_2))连通的条件是:

    存在两条长度相同的可以经过重复点的路径,使得第一条路径的边在(G_1)上,起点为(u_1),终点为(v_1),第二条路径在(G_2)上,起点为(u_2),终点为(v_2)

    注意到路径长度(此处均可以经过重复点)在充分大的时候只与图是否是二分图,以及两个点是否在同一区域里有关。通过对路径奇偶性的分析容易得出下列结论:

    如果两条路径的长度单独奇偶性相同,那么可以让短的路径在某条边上反复横条使得路径长度与长的路径相同。所以只需要考虑路径长度的奇偶性固定的图,也就是二分图。

    • (G_1, G_2)都不是二分图的时候,形成一个非二分图的连通块。

    • (G_1, G_2)有恰好一个二分图的时候,形成一个二分图的连通块。

    • (G_1, G_2)全是二分图的时候,形成两个二分图的连通块。

    于是,我们发现对于一张图,只需要关心孤立点、二分图的连通块、非孤立点的连通块的性质!

    假设这(n)个图中有(a_1, a_2, ..., a_n)的非孤立点,那么孤立点对连通块的贡献为:(prod_{i = 1}^{n} m_i - prod_{i = 1}^{n} a_i)

    这一部分我们只需计算出(a_1, ..., a_n)的期望。计算每个点是孤立点的概率,容易得到(E(a_i) = [1 - (frac{1}{2})^{m_i - 1})]m_i)

    接下来处理非孤立点的贡献。现在设这(n)个图都没有孤立点,我们对每个图记二元组((A_i, B_i))(A_i)表示(G_i)的连通块个数,(B_i)表示(G_i)的二分图的连通块的个数。

    那么根据前面的结论,(G_1 imes G_2)对应的二元组是((A_1B_1 + A_2B_2, A_1B_2 + A_2B_1))。这正好是异或卷积的形式!所以我们只需要求出(A_i + B_i)(A_i - B_i)的期望,再乘起来做IFWT即可。

    手动FWT,仅仅是个常数优化。

    (M = max_{i = 1}^{n} m_i)(F(x) = sum_{i = 0}^{M} 2^{frac{i(i - 1)}{2}} frac{x^i}{i!})。则(ln F(x))的每项系数就是(i)阶连通图的个数的EGF。(F(x) ln F(x))就是(i)阶图总连通块数的EGF。于是(A_i)就求出来了。

    (G(x) = sum_{i = 0}^{M} x^i sum_{j = 0}^{i} frac{2^{j(i - j)}}{j!(i - j)!}),它的每一项意义表示把(i)阶图的每个点二染色以及连边的情况数。那么(frac{ln G(x)}{2})就是(i)阶连通二分图的个数。那么(F(x)frac{ln G(x)}{2})就是(i)阶图的总的二分图的连通块数的EGF。

    现在唯一的问题就是求出(G(x))了。这只需要把(j(i - j))看成(frac{i(i - 1)}{2} - frac{j(j - 1)}{2} - frac{(i - j)(i - j - 1)}{2}),然后做一次卷积即可!

    (i)阶图的孤立点个数的EGF是(F(x)x)

    时间复杂度(O(M log M)),可以获得(100)分。

    CO int N=1<<18;
    int omg[2][N],rev[N];
    int fac[N],inv[N],ifac[N];
    
    void NTT(poly&a,int dir){
    	int lim=a.size(),len=log2(lim);
    	for(int i=0;i<lim;++i){
    		int r=rev[i]>>(18-len);
    		if(i<r) swap(a[i],a[r]);
    	}
    	for(int i=1;i<lim;i<<=1)
    		for(int j=0;j<lim;j+=i<<1)for(int k=0;k<i;++k){
    			int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]);
    			a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
    		}
    	if(dir){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    	}
    }
    poly operator~(poly a){
    	int n=a.size();
    	poly b={fpow(a[0],mod-2)};
    	a.resize(1<<(int)ceil(log2(n)));
    	for(int lim=2;lim<2*n;lim<<=1){
    		poly c(a.begin(),a.begin()+lim);
    		c.resize(lim<<1),NTT(c,0);
    		b.resize(lim<<1),NTT(b,0);
    		for(int i=0;i<lim<<1;++i) b[i]=mul(2+mod-mul(c[i],b[i]),b[i]);
    		NTT(b,1),b.resize(lim);
    	}
    	return b.resize(n),b;
    }
    poly log(poly a){
    	int n=a.size();
    	poly b=~a;
    	for(int i=0;i<n-1;++i) a[i]=mul(a[i+1],i+1);
    	a.resize(n-1);
    	int lim=1<<(int)ceil(log2(2*n-2));
    	a.resize(lim),NTT(a,0);
    	b.resize(lim),NTT(b,0);
    	for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
    	NTT(a,1),a.resize(n);
    	for(int i=n-1;i>=1;--i) a[i]=mul(a[i-1],inv[i]);
    	return a[0]=0,a;
    }
    poly operator*(poly a,poly b){
    	int n=a.size()+b.size()-1,lim=1<<(int)ceil(log2(n));
    	a.resize(lim),NTT(a,0);
    	b.resize(lim),NTT(b,0);
    	for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
    	NTT(a,1),a.resize(n);
    	return a;
    }
    
    int a[N];
    poly f,g,h;
    
    int main(){
    	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    	omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
    	rev[0]=0,rev[1]=1<<17;
    	fac[0]=fac[1]=1;
    	inv[0]=inv[1]=1;
    	ifac[0]=ifac[1]=1;
    	for(int i=2;i<N;++i){
    		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
    		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    		rev[i]=rev[i>>1]>>1|(i&1)<<17;
    		fac[i]=mul(fac[i-1],i);
    		inv[i]=mul(mod-mod/i,inv[mod%i]);
    		ifac[i]=mul(ifac[i-1],inv[i]);
    	}
    	int n=read<int>(),m=0;
    	for(int i=1;i<=n;++i) m=max(m,read(a[i]));
    	
    	f.resize(m+1);
    	for(int i=0;i<=m;++i)
    		f[i]=mul(fpow(2,(int64)i*(i-1)/2%(mod-1)),ifac[i]);
    	
    	g=f*log(f),g.resize(m+1);
    	
    	h.resize(m+1);
    	for(int i=0;i<=m;++i)
    		h[i]=mul(fpow(inv[2],(int64)i*(i-1)/2%(mod-1)),ifac[i]);
    	h=h*h,h.resize(m+1);
    	for(int i=0;i<=m;++i)
    		h[i]=mul(h[i],mul(f[i],fac[i]));
    	h=f*log(h),h.resize(m+1);
    	for(int i=0;i<=m;++i)
    		h[i]=mul(h[i],inv[2]);
    	
    	for(int i=0;i<=m;++i)
    		f[i]=mul(f[i],fac[i]),g[i]=mul(g[i],fac[i]),h[i]=mul(h[i],fac[i]);
    	for(int i=1;i<=m;++i){ // no isolated vertex
    		g[i]=add(g[i],mod-mul(f[i-1],i));
    		h[i]=add(h[i],mod-mul(f[i-1],i));
    	}
    	int ans=0,sum=1;
    	for(int i=1;i<=n;++i)
    		sum=mul(sum,mul(f[a[i]],a[i]));
    	ans=sum,sum=1;
    	for(int i=1;i<=n;++i)
    		sum=mul(sum,mul(f[a[i]]+mod-f[a[i]-1],a[i]));
    	ans=add(ans,mod-sum),sum=1;
    	for(int i=1;i<=n;++i)
    		sum=mul(sum,g[a[i]]+h[a[i]]);
    	ans=add(ans,mul(sum,inv[2])),sum=1;
    	for(int i=1;i<=n;++i)
    		sum=mul(sum,g[a[i]]+mod-h[a[i]]);
    	ans=add(ans,mul(sum,inv[2]));
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    NodeJS NPM 镜像使用方法
    用for; while...do; do...while; 写出九九乘法表
    create-react-app创建的项目中registerServiceWorker.js文件的作用
    前端应该从哪些方面优化网站?
    JS基础整理面试题
    netcore实践:跨平台动态加载native组件
    iOS开发--Swift RAC响应式编程初探
    算法导论学习笔记 (页码:9 ~ 16)
    iOS开发-- 通过runtime kvc 移除导航栏下方的阴影效果线条
    iOS开发--面试
  • 原文地址:https://www.cnblogs.com/autoint/p/13388934.html
Copyright © 2011-2022 走看看