at_abc314_f 题解

题意:

\(n\) 名球员,进行 \(n-1\) 场比赛。对于每场比赛,给定信息 \(p,q\)。那么这场比赛的参赛方为编号为 \(p, q\) 的球员所在的队伍,设为 \(a,b\) 队,分别有 \(|a|,|b|\) 人。那么 \(a\) 队与 \(b\) 对的获胜概率分别为 \(\dfrac{|a|}{|a|+|b|}\)\(\dfrac{|b|}{|a|+|b|}\)。每场比赛后参赛两队将合并为一支新的队伍。现请求出拥有第 \(i\) 号球员的队伍的获胜概率的期望。对 \(998244353\) 取模。

分析:

对于每一个点,我们需要维护他所属的队伍与他的队伍的获胜信息,考虑使用并查集维护所属集合和路径上的信息。具体操作为:对于 \(p, q\) 查他在并查集上的祖先 \(f_p, f_q\)。计算概率并将 \(tag\) 打在祖先上。这样对于每一个点,我们只需要一直查他的祖先,并将路径上的 \(tag\) 值加起来,则得到一个点的答案。

由于需要路径上的信息,不建议使用路径压缩。但是看一眼数据范围,暴力是过不了的,因此使用按秩合并。数据范围不需要我们线性地求逆元,只需要快速幂即可。

注意:在合并 \(x, y\) 两个集合时(例如:将 \(x\) 集并在 \(y\) 集上),由于合并前 \(y\) 集的信息不应包含 \(x\) 集。因此在合并时应给 \(tag_x\) 减上 \(tag_y\) 这样能保证查出来的数据是正确的。

代码:

#include<bits/stdc++.h>
#define int long long //没把握就define全局long long
using namespace std;
int n, p, q; const int mod = 998244353;
int siz[200005], fa[200005], tag[200005];//并查集数组
inline int find(int x){ //查祖先
    if(x == fa[x]) return x;
    return find(fa[x]);
}
inline void merge(int x, int y){ //合并
    if(siz[x] <= siz[y]){ //按秩合并
        fa[x] = y;
        siz[y] += siz[x];
        tag[x] = (tag[x] - tag[y] + mod) % mod; //记得减tag和取模
    }
    else{
        fa[y] = x;
        siz[x] += siz[y];
        tag[y] = (tag[y] - tag[x] + mod) % mod;
    }
} 
inline int qpow(int a, int b){
    int res = 1;
    while(b){
        if(b & 1) res = res * a % mod;
        b >>= 1; a = a * a % mod;
    }return res;
}
inline int cacl(int p){ //计数
    int res = 0;
    while(p != fa[p]) res = (res + tag[p]) % mod, p = fa[p];
    return (res + tag[p])%mod; //别忘了加根
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0); cin >> n;//一定注意要读入(麻赛时没读入wa了两发最后也没调出来)
    for(int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1; //初始化
    for(int i = 1 && cin >> p >> q; i < n; i++ && cin >> p >> q){ //顺带读入
        int x = find(p), y = find(q);
        int sum = siz[x] + siz[y]; int inv = qpow(sum, mod - 2); //逆元
        tag[x] = (tag[x] + siz[x] * inv) % mod;
        tag[y] = (tag[y] + siz[y] * inv) % mod; //打tag
        merge(x, y); //合并
    }
    for(int i = 1; i <= n; i++){
        cout << cacl(i) << ' ';
    }return 0;
}
posted @ 2023-10-01 01:17  xlpg0713  阅读(18)  评论(0)    收藏  举报