撿石頭——(期望遞推)
阿新 • • 發佈:2018-12-22
描述
XXX發現他生活的村有很多值錢的石頭。XXX 所在的村有 N 堆石頭,第 i 堆的價值為 Vi。另有 M 條單向道路連線這些石頭堆,滿足從任意一堆石頭出發沿著這些道路不會回到 起點。XXX 打算來一次撿石頭之旅。由於他沒有地圖,他無法知道應該怎麼走。因此他 只能採取這樣的策略:每次從當前所在石頭堆等概率隨機選擇一條可走的道路,走過去。 XXX 到達一堆石頭後就會立刻把它們撿進自己的包裡面。初始時,XXX 將等概率隨機 空降到一個石頭堆。現在,xxx向請你幫他算出他期望能得到多少價值的石頭。
輸入
第一行兩個整數 N, M ,分別為石頭堆數和單向道路數。 接下來一行 N 個正整數表示 Vi。 接下來 M 行,每行兩個整數 a b,表示一條從 a 到 b 的單向道路。
輸出
輸出一行一個實數表示答案。四捨五入保留兩位小數即可。
樣例輸入
1 0
1
樣例輸出
1.00
提示
對於 50%的資料, N, M<=100 。 對於 100%的資料,1<= N, M <=10^5,1<=V i<=10^4
由於是個DAG
所以直接建反向邊,拓撲排序,直接向父親傳遞資訊就是了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read(){
char ch=getchar();
int res=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar ();
return res*f;
}
const int N=100005;
int adj[N],nxt[N],to[N],a[N],n,m,in[N],cnt,siz[N];
inline void addedge(int u,int v){
nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
double ans,hop[N];
queue<int> q;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=m;i++ ){
int u=read(),v=read();
addedge(v,u),in[u]++;
}
for(int i=1;i<=n;i++){
if(in[i]==0)q.push(i);
}
while(!q.empty()){
int u=q.front();q.pop();
if(siz[u])hop[u]/=(double)(siz[u]);
hop[u]+=a[u];
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
siz[v]++,hop[v]+=hop[u],in[v]--;
if(in[v]==0)q.push(v);
}
}
for(int i=1;i<=n;i++){
ans+=hop[i];
}
ans=ans/((double)(n*1.0));
printf("%.2lf",ans);
}