【2019年徐州网络赛J题】【Random Access Iterator】【概率/树形dp】

题目链接:
https://nanti.jisuanke.com/t/41392
题意:
现在给一颗以点1为根节点的树,对于当前节点,每一个子节点都有等概率访问,现在访问子节点个数次,问从根节点开始至少能到达一次最深的节点的概率是多少(概率还要求逆元)
题解:
这里我们记节点 i i 到达最深节点的概率为 p [ i ] p[i] (不妨就记为成功概率), c n t [ i ] cnt[i] 为当前节点的子节点个数
那么对于一个点 i i ,我们访问一遍子节点成功的概率为(等概率访问每个子节点所以乘 1 c n t [ i ] \frac{1}{cnt[i]} )
n o w = 1 c n t [ i ] s o n = 1 c n t [ i ] p [ s o n ] now=\frac{1}{cnt[i]}\displaystyle\sum_{son=1}^{cnt[i]} p[son]
那么访问一次失败的概率为
1 n o w 1-now
所以访问 c n t [ i ] cnt[i] 次,至少成功一遍的概率 = = 全部情况 - 全部失败的概率
1 ( 1 n o w ) c n t [ i ] 1-(1-now)^{cnt[i]}

最终我们用两遍 d f s dfs 即可求解,还有题目要求所有分数用逆元表示(具体可见代码)
第一遍 d f s dfs 求最大深度
第二遍 d f s dfs 求概率,从叶节点开始转移,显然处于最大深度的叶节点 p [ i ] = 1 p[i]=1

代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define sz sizeof
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e6 + 10;
const int mod = 1e9 + 7;

int N;

ll quick_pow(ll x, ll k) {
	ll res = 1;
	while (k) {
		if (k & 1)
			(res *= x) %= mod;
		(x *= x) %= mod;
		k >>= 1;
	}
	return res;
}

ll inv(ll x) { return quick_pow(x, mod - 2); }

struct edge {
	int nxt, to;
} e[MAX * 2];

int tot, head[MAX], dep[MAX], cnt[MAX], maxx;
ll p[MAX];

void add(int u, int v) {
	e[++tot].to = v;
	e[tot].nxt = head[u];
	head[u] = tot;
}

void dfs(int u, int d, int pre) {
	dep[u] = d;
	maxx = max(maxx, d);
	for (int i = head[u]; i; i = e[i].nxt) {
		int son = e[i].to;
		if (son == pre) continue;
		cnt[u]++;
		dfs(son, d + 1, u);
	}
}

void solve(int u, int pre) {
	if (cnt[u] == 0 && dep[u] == maxx) {//到达叶节点,如果为最大深度那概率就是1,否则为0
		p[u] = 1;
		return;
	}
	ll now = 0;
	//这里所有结果都直接用逆元来求
	for (int i = head[u]; i; i = e[i].nxt) {
		int son = e[i].to;
		if (son == pre) continue;
		solve(son, u);
		(now += p[son]) %= mod;//加上子节点的概率
	}
	(now *= inv(cnt[u])) %= mod;//除以cnt[i],为单次成功
	now = (1 - now + mod) % mod;//变为单次失败
	p[u] = (1 - quick_pow(now, cnt[u]) + mod) % mod;//重复cnt[i]次至少成功一次
}

int main() {
#ifdef ACM_LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> N;
	for (int i = 1; i < N; i++) {
		int u, v;
		cin >> u >> v;
		//加边
		add(u, v);
		add(v, u);
	}
	dfs(1, 1, 0);//求最大深度,用maxx记
	solve(1, 0);//求概率
	cout << p[1] << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44282912/article/details/100607701