題解 lg3232 [HNOI2013]遊走
阿新 • • 發佈:2020-11-18
題意
給定一個 n個點 m 條邊的無向連通圖,頂點從 1 編號到 n,邊從 1編號到 m。
小 Z 在該圖上進行隨機遊走,初始時小 Z 在 1號頂點,每一步小 Z 以相等的概率隨機選擇當前頂點的某條邊,沿著這條邊走到下一個頂點,獲得等於這條邊的編號的分數。當小 Z 到達 n 號頂點時遊走結束,總分為所有獲得的分數之和。 現在,請你對這 m 條邊進行編號,使得小 Z 獲得的總分的期望值最小。
思路
首先,一個較為顯然的性質, 獲得的總分的期望值 就是 每一條邊期望經過的次數 乘上 其權值
然後貪心地將小權值賦給期望次數大的邊
但邊數實際上最大會有\(125000\),會跑不過,我們此時運用技巧點邊轉換
其中\(deg\)為點的度數,\(u,v\)分別為一條邊的兩個端點,\(f\)為點的期望經過次數
然後考慮點,對於點n,它不會被經過,因為到它就停了,於是有以下式子
\[f_i=\sum_{e(j,i)\in E,j\neq n} \frac{f_j}{d_j} \]注意,當\(i=1\)時,需特判為\(f_1=\sum_{e(j,1)\in E,j\neq n} \frac{f_j}{d_j}+1\),因為從1開始.
然後就發現不能遞推QAQ
但是這可以轉換一個n-1元1次方程組,於是高斯消元
程式碼
#include<bits/stdc++.h> using namespace std; int n,m; int d[510]; double a[510][510],c[510],val[125010],x[510]; double ans=0.0; struct edge{ int x,y; }e[125010]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&e[i].x,&e[i].y); d[e[i].x]++,d[e[i].y]++; } for(int i=1;i<n;i++)a[i][i]=1.0,c[i]=0.0; for(int i=1;i<=m;i++){ if(e[i].x==n || e[i].y==n)continue; a[e[i].x][e[i].y]=-1.0/d[e[i].y]; a[e[i].y][e[i].x]=-1.0/d[e[i].x]; } c[1]=1.0; for(int i=1;i<=n-1;i++){ int maxx=i; for(int j=i+1;j<=n-1;j++) if(fabs(a[maxx][i])<fabs(a[j][i]))maxx=j; if(maxx!=i)swap(a[i],a[maxx]),swap(c[i],c[maxx]); for(int j=1;j<=n-1;j++){ if(j==i)continue; double rate=a[j][i]/a[i][i]; for(int k=i;k<=n-1;k++)a[j][k]-=rate*a[i][k]; c[j]-=rate*c[i]; } } for(int i=1;i<=n-1;i++)x[i]=c[i]/a[i][i]; for(int i=1;i<=m;i++){ if(e[i].x!=n)val[i]+=1.0*x[e[i].x]/d[e[i].x]; if(e[i].y!=n)val[i]+=1.0*x[e[i].y]/d[e[i].y]; } sort(val+1,val+m+1); for(int i=1;i<=m;i++){ ans+=(m-i+1)*val[i]; } printf("%.3lf\n",ans); return 0; }