CF1236F Alice and the Cactus 期望

博客详细介绍了CF1236F题目的解决方案,主要聚焦于连通块个数的期望和方差计算。通过将连通块个数表示为点数、边数和环的关系,博主探讨了如何求解期望值E(x2)。文章讨论了如何分别计算E(x),E(a2),E(b2),E(c2),E(-2ab),E(2ac)和E(-2bc)的期望,并给出了边数乘以环的个数期望值的计算方法,涉及概率计算。最后,博主提到了整体的时间复杂度为O(n+m)。

题面

题目传送门

题解

  • 考虑连通块的个数为点数-边数+环的个数,方差的式子显然可以被化成 E ( x 2 ) − E ( x ) 2 E(x^2)-E(x)^2 E(x2)E(x)2( x x x为连通块个数)。 E ( x ) E(x) E(x)很好求,直接计算相应的期望就可以了。
  • 然后问题就变成了如何求 E ( x 2 ) E(x^2) E(x2),其中 x = a − b + c x=a-b+c x=ab+c,把式子打开有 E ( x 2 ) = E ( a 2 + b 2 + c 2 − 2 a b + 2 a c − 2 b c ) E(x^2)=E(a^2+b^2+c^2-2ab+2ac-2bc) E(x2)=E(a2+b2+c22ab+2ac2bc),对应的期望分别求一下就可以了。
  • 举个例子,比如说我们现在需要计算边数×环的个数的期望值,那么这个值也等于一条边与一个环同时存在的概率之和。至于求这个值,可以考虑枚举每一个环,考虑在环上的边、与环有交的边以及与环没有交的边,计算一下相应的概率即可。
  • 其他情况不再赘述,也比较简单。
  • 时间复杂度: O ( n + m ) O(n+m) O(n+m)

代码

#include <bits/stdc++.h>
#define pb push_back
#define ll long long
using namespace std;
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 500010, mod = 1e9 + 7, inv2 = 5e8 + 4;
int n, m, l, id[N], pw[N], st[N], vis[N], inv[N];
vector <int> e[N], in[N]; vector <vector <int> > cyc;
int sum(int x, int y) {return x + y >= mod ? x + y - mod : x + y;}
int sub(int x, int y) {return x - y < 0 ? x - y + mod : x - y;}
int mul(int x, int y) {return 1ll * x * y % mod;}
void dfs(int x, int las = 0) {
	vis[x] = 1, st[id[x] = ++l] = x;
	for (int y : e[x]) {
		if (vis[y] == 2) continue;
		if (y == las) continue;
		if (vis[y]) {
			vector <int> tmp; tmp.clear();
			for (int i = id[y]; i <= l; i++) tmp.pb(st[i]);
			cyc.pb(tmp);
		} else dfs(y, x);
	}
	vis[x] = 2, st[l--] = 0;
}
int ver() {return sum(mul(n, inv[1]), mul(mul(n, n - 1), inv[2]));}
int edge() {
	int ret = mul(m, inv[2]);
	for (int x = 1; x <= n; x++)
		for (int y : e[x]) {
			if (x > y) continue; 
			int s1 = m - e[x].size() - e[y].size() + 1, s2 = e[x].size() + e[y].size() - 2;
			ret = sum(ret, mul(s1, inv[4])), ret = sum(ret, mul(s2, inv[3]));
		}
	return ret;
}
int cycle() {
	int tmp = 0, ret = 0;
	for (auto cir : cyc) tmp = sum(tmp, inv[cir.size()]);
	for (auto cir : cyc) {
		int siz = cir.size(), val = tmp; ret = sum(ret, inv[siz]);
		for (int x : cir) {
			for (int s : in[x]) val = sub(val, inv[s]);
			val = sum(val, inv[siz]);
		}
		val = sub(val, inv[siz]), ret = sum(ret, mul(inv[siz], val));
		int res = sub(tmp, sum(val, inv[siz]));
		ret = sum(ret, mul(mul(res, 2), inv[siz]));
	}
	return ret;
}
int veredge() {
	int ret = 0;
	for (int i = 1; i <= n; i++) {
		int s1 = e[i].size(), s2 = m - e[i].size();
		ret = sum(ret, mul(s1, inv[2])), ret = sum(ret, mul(s2, inv[3]));
	}
	return ret;
}
int vercycle() {
	int tmp = 0, ret = 0;
	for (auto cir : cyc) tmp = sum(tmp, inv[cir.size()]);
	for (int i = 1; i <= n; i++) {
		int ts = 0;
		for (int x : in[i]) ts = sum(ts, inv[x]);
		ret = sum(ret, ts), ret = sum(ret, mul(sub(tmp, ts), inv[1]));
	}
	return ret;
}
int edgecycle() {
	int ret = 0;
	for (auto cir : cyc) {
		int siz = cir.size(), ts = 0; ret = sum(ret, mul(siz, inv[siz]));
		for (int x : cir) ts += e[x].size() - 2;
		ret = sum(ret, mul(ts, inv[siz + 1]));
		ret = sum(ret, mul(m - siz - ts, inv[siz + 2]));
	}
	return ret;
}
int calc1() {
	int s1 = sum(ver(), sum(edge(), cycle())), s2 = sub(vercycle(), sum(veredge(), edgecycle()));
	return sum(s1, mul(s2, 2));
}
int calc2() {
	int ret = sub(mul(n, inv[1]), mul(m, inv[2]));
	for (auto cir : cyc) ret = sum(ret, inv[cir.size()]);
	return mul(ret, ret);
}
int main() {
	read(n), read(m), pw[0] = inv[0] = 1;
	for (int i = 1; i <= 5e5; i++) pw[i] = mul(pw[i - 1], 2), inv[i] = mul(inv[i - 1], inv2);
	for (int i = 1; i <= m; i++) {
		int x, y; read(x), read(y);
		e[x].pb(y), e[y].pb(x);
	}
	dfs(1);
	for (auto cir : cyc) for (int i : cir) in[i].pb(cir.size());
	cout << (calc1() - calc2() + mod) % mod << '\n';
	return 0;
}
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值