题目链接:
https://nanti.jisuanke.com/t/41392
题意:
现在给一颗以点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;
}