Codeforces 1236F

Codeforces 题面传送门 & 洛谷题面传送门

期望好题。

首先拆方差:

[egin{aligned} &E((x-E(x))^2)\ =&E(x^2)-2E(x)E(E(x))+E(E(x)^2)\ =&E(x^2)-E(x)^2 end{aligned} ]

因此我们只需 (E(x^2))(E(x)) 即可求解出答案。

考虑 (x) 是什么东西。直接从连通块个数的角度下手异常棘手。不过注意到原图是一个仙人掌,因此假设点数、边数、环数分别为 (a,b,c),那么连通块个数 (x=a-b+c),这可以看作原本有 (a) 个连通块,每加入一条边会少一个连通块,但有 (c) 次加边时成环,连通块个数不变。

因此 (E(x^2)=E((a-b+c)^2)=E(a^2)+E(b^2)+E(c^2)-2E(ab)+2E(ac)-2E(bc))(E(x)=E(a)-E(b)+E(c))

考虑对这九个东西分别计算:

1. (E(a))

显然 (E(a)=dfrac{n}{2})

2. (E(b))

显然 (E(b)=dfrac{m}{4}),因为每条边有 (dfrac{1}{4}) 的概率存在。

3. (E(c))

假设原仙人掌中第 (i) 个环点数为 (C_i),那么显然第 (i) 个环被保留的概率就是 (p_i=dfrac{1}{2^{C_i}})

期望环数 (=sumlimits_{i=1}^cp_i),其中 (c) 为原仙人掌中环数。

4. (E(a^2))

根据期望的线性性 (E(a^2)) 等于每对点同时存在的概率。

考虑对于两个点 (i,j)

  • 如果 (i=j),那它们同时存在的概率为 (dfrac{1}{2})
  • 否则它们同时存在的概率为 (dfrac{1}{4})

化简一下可得 (E(a^2)=dfrac{n(n+1)}{4})

5. (E(b^2))

根据期望的线性性 (E(b^2)) 等于每对边同时存在的概率。

考虑两条边 (i,j)

  • 如果 (i=j),那它们同时存在的概率为 (dfrac{1}{4})
  • 如果 (i,j) 两条边恰好存在一个公共端点,那么它们同时存在的概率为 (dfrac{1}{8})
  • 否则它们同时存在的概率为 (dfrac{1}{16})

对于第一种情况贡献就是 (E(b)),直接加上,对于第二种情况就枚举公共点,这样可以算得有多少对边符合第二种情况,剩余的边肯定都属于第三种情况了。

6. (E(c^2))

根据期望的线性性 (E(c^2)) 等于每对环同时存在的概率。

考虑两个环 (i,j),由于原图是仙人掌两个不同的环最多只有一个公共点:

  • 如果 (i=j),那它们同时存在的概率为 (p_i)
  • 如果 (i,j) 两个环恰好存在一个公共点,那它们同时存在的概率为 (p_i·p_j·2)
  • 否则它们同时存在的概率为 (p_i·p_j)

(E(b^2)) 的求法类似,第一种情况的贡献就是 (E(c)),对于二三两种情况我们先额外将所有 (i e j)(p_i·p_j) 加入答案,再额外枚举所有符合第二类限制的两个环 (i,j),再将它们的 (p_i·p_j) 累加入答案。具体求法也是枚举公共点然后扫一遍,并维护当前扫过的所有环的 (p_i) 之和即可算出贡献。

7. (E(ab))

根据期望的线性性 (E(ab)) 等于每个点与每条边同时存在的概率之和。

考虑一个点 (a) 和一条边 (e)

  • 如果 (a)(e) 的一个端点,那么它们同时存在的概率为 (dfrac{1}{4})
  • 否则它们同时存在的概率为 (dfrac{1}{8})

枚举每个点,然后预处理每个点的度即可算出有多少条边符合第一种情况,有多少条边符合第二种情况。

8. (E(ac))

根据期望的线性性 (E(ac)) 等于每个点与每个环同时存在的概率之和。

考虑一个点 (a) 和一个环 (c)

  • 如果 (a)(c) 上,那它们同时存在的概率为 (p_c)
  • 否则它们同时存在的概率为 (dfrac{1}{2}p_c)

还是按照套路枚举环 (c),预处理每个环上的点数即可知道有多少个点符合第一种情况,有多少个点符合第二种情况。

9. (E(bc))

根据期望的线性性 (E(bc)) 等于每条边与每个环同时存在的概率之和。

考虑一条边 (e) 和一个环 (c)

  • 如果 (e) 的两个端点都在 (c) 上,那它们同时存在的概率为 (p_c)
  • 如果 (e) 的一个端点在 (c) 上,那它们同时存在的概率为 (dfrac{1}{2}p_c)
  • 如果 (e) 的两个端点都不在 (c) 上,那它们同时存在的概率为 (dfrac{1}{4}p_c)

对于第一种情况还是通过预处理每个环上的点数即可快速算出,第二种情况符合条件的边数即为环上每个点的度数减 (2) 之和,剩余的边都是第三种情况。

时间复杂度 (mathcal O(n+m))

const int MAXN=5e5;
const int MOD=1e9+7;
const int INV2=MOD+1>>1;
const int INV4=MOD+1>>2;
const int INV8=MOD+1>>3;
const int INV16=((MOD+9>>4)-INV2+MOD)%MOD;
int n,m,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dfn[MAXN+5],fa[MAXN+5],tim=0,ipw2[MAXN+5];
vector<int> cyc[MAXN+5],bel[MAXN+5];
int cycnt=0,P[MAXN+5],deg[MAXN+5];
void dfs(int x){
	dfn[x]=++tim;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];
		if(!dfn[y]) fa[y]=x,dfs(y);
		else if(dfn[y]<dfn[x]&&y!=fa[x]){
			cycnt++;
			for(int i=x;i^y;i=fa[i]) cyc[cycnt].pb(i),bel[i].pb(cycnt);
			cyc[cycnt].pb(y);bel[y].pb(cycnt);
		}
	}
}
int Ea(){return 1ll*n*INV2%MOD;}
int Eb(){return 1ll*m*INV4%MOD;}
int Ec(){int sum=0;for(int i=1;i<=cycnt;i++) sum=(sum+P[i])%MOD;return sum;}
int Ex(){return (0ll+Ea()-Eb()+Ec()+MOD)%MOD;}
int Eaa(){return 1ll*n*(n+1)%MOD*INV4%MOD;}
int Ebb(){
	ll tot=1ll*m*(m-1);int res=1ll*m*INV4%MOD;
	for(int i=1;i<=n;i++){
		tot-=1ll*deg[i]*(deg[i]-1);
		res=(res+1ll*deg[i]*(deg[i]-1)%MOD*INV8)%MOD;
	} res=(res+1ll*tot%MOD*INV16)%MOD;
	return res;
}
int Ecc(){
	ll tot=1ll*cycnt*(cycnt-1);int res=Ec(),sum=0;
	for(int i=1;i<=cycnt;i++) res=(res+2ll*sum*P[i])%MOD,sum=(sum+P[i])%MOD;
	for(int i=1;i<=n;i++){
		int ssum=0;
		for(int j:bel[i]) res=(res+2ll*ssum*P[j])%MOD,ssum=(ssum+P[j])%MOD;
	} return res;
}
int Eab(){
	int res=0;
	for(int i=1;i<=n;i++) res=(res+1ll*INV4*deg[i]+1ll*INV8*(m-deg[i]))%MOD;
	return res;
}
int Eac(){
	int res=0,sm=Ec();
	for(int i=1;i<=n;i++){
		int ssum=0;
		for(int j:bel[i]) ssum=(ssum+P[j])%MOD;
		res=(0ll+res+ssum+1ll*INV2*(sm-ssum+MOD))%MOD;
	} return res;
}
int Ebc(){
	int res=0;
	for(int i=1;i<=cycnt;i++) res=(res+1ll*P[i]*cyc[i].size())%MOD;
	for(int i=1;i<=cycnt;i++){
		int sum_ed=0,rst;
		for(int j:cyc[i]) sum_ed+=deg[j]-2;
		rst=m-cyc[i].size()-sum_ed;
		res=(0ll+res+1ll*P[i]*INV2%MOD*sum_ed+1ll*P[i]*INV4%MOD*rst)%MOD;
	} return res;
}
int sqr(int x){return 1ll*x*x%MOD;}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=(ipw2[0]=1);i<=n;i++) ipw2[i]=1ll*ipw2[i-1]*INV2%MOD;
	for(int i=1,u,v;i<=m;i++){
		scanf("%d%d",&u,&v);adde(u,v);adde(v,u);
		deg[u]++;deg[v]++;
	} dfs(1);for(int i=1;i<=cycnt;i++) P[i]=ipw2[cyc[i].size()];
//	printf("%d
",Ec());
	printf("%d
",((0ll+Eaa()+Ebb()+Ecc()-2ll*Eab()-2ll*Ebc()+2ll*Eac()-sqr(Ex()))%MOD+MOD)%MOD);
	return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/Codeforces-1236F.html