题目链接:Problem - 7450 (hdu.edu.cn)
Problem Description
传说在遥远的魔法大陆有着一个村庄叫做卡卡奇里奇,这里有n座房屋,n−1条道路,保证了任意两座房屋之间都可以通过道路相互可达。这里环境优美,居民幸福地生活着。
但是有一天晚上,原本平静的村庄突然受到一不明寄生生命体的袭击,奇怪的怪物绑架了所有居民,并控制了所有房屋。凌晨时分,在外游历的勇士小凯收到了卡卡奇里奇国王的召唤来到了卡卡奇里奇,奉命消灭所有怪物,解救整个村庄。
由于有人工智能Fairy的存在,卡卡奇里奇国王能够知道小凯和每个房屋的怪物的较量中的获胜概率是多少。在一场小凯和第i个房屋的怪物较量中,小凯有pi15的概率获得胜利,成功消灭第i个房屋的所有怪物,同时小凯有1−pi15的概率失败,那么第i个房屋的怪物会依旧存在,只能之后再挑战。由于没有被消灭怪物会在每个晚上恢复元气,所以每一天小凯对第i个房屋的怪物的战斗胜率是固定的。
经过了长途跋涉之后,小凯来到了卡卡奇里奇的**1号房屋**开始了战斗。每一天白天,小凯都会对当前他所在的房屋的怪物发起挑战,如果成功那么他会询问卡卡奇里奇国王然后在国王的建议之下选择一个**与当前房屋相邻的(有直接的道路相连的)**、**还存在怪物**的房屋前进(但是第二天才能对该房屋的怪物进行挑战),如果不存在这样的房屋,那么喜欢摸鱼的小凯便会**离开这个村庄**去摸鱼。如果挑战失败,那么小凯会在这个房屋门口休息一个晚上,等到下一天继续发起挑战。
国王希望摸鱼的小凯在村庄呆的久一点,所以他想问你在他的控制之下,小凯最多期望在村庄里停留多少天?请你以最简分数的形式告诉他这个答案。
Input
第一行一个整数T (1≤T≤20)​,表示数据组数。
对于每组数据,第一行有一个整数n (1≤n≤105),表示房屋的数量。
接下来有n−1行,每行两个整数u,v,表示村庄的一条道路,保证**没有重边,没有自环**。(1≤u,v≤n,u≠v)​
接下来有n个整数,第i个整数pi (1≤pi≤15)表示小凯打倒第i个村庄的怪物的概率为pi15。
数据保证∑n≤5⋅105
Output
对于每组数据,输出一个最简分数(A/B,A与B互质)表示小凯最多期望在村庄里停留多少天。数据保证答案不会为0。
解题思路:对于每一个点,小凯能够打赢的概率都是,根据期望公式,可以求得每个点期望。又因为每个点,因此我们可以预处理这15个期望,然后再通过一遍dijkstra,去寻找最大的边就行。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=1e5+5;
int n;
int head[N],h[N],dis[N],vis[N],cnt=0;
map<int,int>mp;
vector<pii> v;
struct T{
int to,ne,w;
}s[N*4];
void init(){
cnt=0;
v.clear();
for(int i=0;i<=n;i++){
head[i]=-1;
vis[i]=0;
dis[i]=0;
h[i]=0;
}
}
void add(int u,int v,int w){
s[cnt].w=w;
s[cnt].to=v;
s[cnt].ne=head[u];
head[u]=cnt;
cnt++;
}
struct cmp{
bool operator()(pii x,pii y){
return x.second<y.second; //我们需要的是最大边,所以优先队列从大到小排序。
}
};
void dj(){
dis[1]=h[1];
priority_queue<pii,vector<pii>,cmp> q;
q.push({1,dis[1]});
while(!q.empty()){
int x=q.top().first;
q.pop();
if(vis[x]){
continue;
}
vis[x]=1;
for(int i=head[x];i!=-1;i=s[i].ne){
if(!vis[s[i].to] && dis[s[i].to]<dis[x]+s[i].w){ //与最短路相反
dis[s[i].to]=dis[x]+s[i].w;
q.push({s[i].to,dis[s[i].to]});
}
}
}
}
void solve(){
cin>>n;
init(); //别忘了每一组数据的初始化 !!!!
for(int i=1;i<=n-1;i++){ //由于数据先给你的是每一条边,但是没有权值,所以先存起来
int a,b;
cin>>a>>b;
v.push_back({a,b});
}
for(int i=1;i<=n;i++){ //记录每一个点的期望
int a;
cin>>a;
h[i]=mp[a];
}
for(int i=0;i<v.size();i++){ //每一条边的花费,就可以看成是到达的点的期望
add(v[i].first,v[i].second,h[v[i].second]);
add(v[i].second,v[i].first,h[v[i].first]);
}
dj();
int maxx=0;
for(int i=1;i<=n;i++){
maxx=max(maxx,dis[i]);
}
int sum=__gcd(maxx,(int)360360);
cout<<maxx/sum<<'/'<<360360/sum<<endl;
}
signed main(){
IOS;
for(int i=1;i<=15;i++){ //先预处理1-15的期望
/*由于分母会有存在3,4,5,6等等,
而15/4等并不能除尽,为此我们需要将所有的期望进行通分。
这时候我们就需要找到1-15的最小公倍数,也就是
5*7*8*9*11*13=360360。
这样就保证了分母相同,分子就可以直接相加得到结果
最后再用答案与360360化简即可 */
int x=__gcd(i,(int)360360);
mp[i]=15*360360/i;
}
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}