UVA 12487 (思维,概率+LCA)

本文介绍了一种在给定的树形结构中计算从特定节点出发到达两个目标节点之一的概率的方法。通过找到两目标节点的最近公共祖先(LCA),利用深度信息计算到达一节点先于另一节点的概率。

题意:
给一个n个点树,a,b,c三个点,问从a出发,随机选一个分支前进,问到达b点先于c点的概率。


思路:
b到c有且仅有一条路径,除这条路径外的分支,走进去了也会有同样的概率走出来,所以只需要考虑以a为根,b和c的LCA到b和c的深度。答案为deep(c)/(deep(b)+deep(c))。


代码:

#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <map>
#include <list>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <sstream>
#define pb push_back
#define X first
#define Y second
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pii pair<int,int>
#define qclear(a) while(!a.empty())a.pop();
#define lowbit(x) (x&-x)
#define sd(n) scanf("%d",&n)
#define sdd(n,m) scanf("%d%d",&n,&m)
#define sddd(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define mst(a,b) memset(a,b,sizeof(a))
#define cout3(x,y,z) cout<<x<<" "<<y<<" "<<z<<endl
#define cout2(x,y) cout<<x<<" "<<y<<endl
#define cout1(x) cout<<x<<endl
#define IOS std::ios::sync_with_stdio(false)
#define SRAND srand((unsigned int)(time(0)))
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
using namespace std;
const double PI=acos(-1.0);
const int INF=0x3f3f3f3f;
const ll mod=1000000007;
const double eps=1e-8;
const int maxn=105;
const int maxm=10005;

int n;
int a,b,c;
vector<int>maps[maxn];
int dep[maxn];
bool vis[maxn];
int fa[maxn];
int findfa(int x){
    return fa[x]==x?x:fa[x]=findfa(fa[x]);
}
void meg(int a,int b){
    int f1,f2;
    f1=findfa(a);
    f2=findfa(b);
    if(f1!=f2){
        fa[f2]=f1;
    }
}
void dfs(int u,int d){
    vis[u]=1;
    dep[u]=d;
    for(int i=0;i<maps[u].size();i++){
        int now=maps[u][i];
        if(dep[now]!=-1)continue;
        dep[now]=d+1;
        dfs(now,d+1);
    }
    return ;
}
int u1,u2,lca;
void tarjan(int u){
    vis[u]=1;
    for(int i=0;i<maps[u].size();i++){
        int now=maps[u][i];
        if(vis[now])continue;
        tarjan(now);
        meg(u,now);
    }
    if(u==u1){
        if(vis[u2]){
            lca=findfa(u2);
        }
    }
    if(u==u2){
        if(vis[u1]){
            lca=findfa(u1);
        }
    }
    return ;
}
void solve() {
    while(~sd(n)){
        sddd(a,b,c);
        for(int i=0;i<=n;i++){
            maps[i].clear();
        }
        for(int i=0;i<n-1;i++){
            int u,v;
            sdd(u,v);
            maps[u].pb(v);
            maps[v].pb(u);
        }
        mst(dep,-1);
        dfs(a,0);
        for(int i=0;i<=n;i++)fa[i]=i;
        mst(vis,0);
        u1=b;
        u2=c;
        tarjan(a);
        int db,dc;
        db=dep[b]-dep[lca];
        dc=dep[c]-dep[lca];
        double ans=0;
        if(db==0){
            ans=1;
        }else if(dc==0){
            ans=0;
        }else{
            ans=(double)dc/(double)(db+dc);
        }
        printf("%.6f\n",ans);
    }
    return ;
}
int main() {
#ifdef LOCAL
    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
#else
    //    freopen("","r",stdin);
    //    freopen("","w",stdout);
#endif
    solve();
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值