[补档][Hnoi2013]游走
[Hnoi2013]游走
题目
一个无向连通图,顶点从1编号到N,边从1编号到M。小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。INPUT
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。OUTPUT
仅包含一个实数,表示最小的期望值,保留3位小数。SAMPLE
INPUT
3 32 31 21 3OUTPUT
3.333
解题报告
这东西显然是个概率与期望(题目写的那么清楚啊喂),好吧,是一个很裸的概率与期望。
题目要求总分最小,且编号从1到m,那么显然,我们需要求一下每条边被经过的期望,期望越大,编号越小。
首先自然能删除终点的所有出边(终点是不能出来的),然后,对于每一条边,设两端端点为u,v,我们可以从u走到v,也可以从v走到u,从u走到v的期望次数等于
经过点u的次数/u的度
问题自然就转化成求每个点的期望经过次数,对于起点来说,一开始一定会经过一次,在之后也可能被经过。
f[1]=1+sigma(f[j]/degree[j],j和1有边相连)
f[i]=sigma(f[j]/degree[j],i与j有边相连)
我们得到了n变量n方程的方程组,然后高斯消元乱抡= =
稍微处理下就可以得到经过每个点的期望,那么每条边的期望即为两端点期望之和(注意:对终点一定要特殊处理啊啊啊),对每条边按期望排序,随便一乘,一加,就可以AC了。
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 using namespace std; 6 inline int read(){ 7 int sum(0); 8 char ch(getchar()); 9 for(;ch<'0'||ch>'9';ch=getchar()); 10 for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar()); 11 return sum; 12 } 13 struct edge{ 14 int s,e,n; 15 }ed[250001]; 16 int pre[501],tot; 17 inline void insert(int s,int e){ 18 ed[++tot].s=s; 19 ed[tot].e=e; 20 ed[tot].n=pre[s]; 21 pre[s]=tot; 22 } 23 int du[501]; 24 int n,m; 25 double a[501][501],b[501],ans[501]; 26 inline double jdz(double x){ 27 return x>=0?x:-x; 28 } 29 inline void swp(double &a,double &b){ 30 double c(a); 31 a=b; 32 b=c; 33 } 34 inline void gauss(){ 35 int num,cnt(1); 36 for(int i=1;i<n;i++,cnt++){ 37 num=i; 38 for(int j=i+1;j<=n;j++) 39 if(jdz(a[num][i])<jdz(a[j][i])) 40 num=j; 41 if(num!=i){ 42 for(int j=1;j<=n;j++) 43 swp(a[num][j],a[cnt][j]); 44 swp(b[num],b[cnt]); 45 } 46 if(!a[cnt][i]){ 47 cnt--; 48 continue; 49 } 50 for(int j=cnt+1;j<=n;j++){ 51 double t(a[j][i]/a[cnt][i]); 52 for(int k=i;k<=n;k++) 53 a[j][k]-=t*a[i][k]; 54 b[j]-=t*b[i]; 55 } 56 } 57 for(int i=n;i>0;i--){ 58 for(int j=n;j>i;j--) 59 b[i]-=a[i][j]*ans[j]; 60 ans[i]=b[i]/a[i][i]; 61 } 62 } 63 double f[250001]; 64 bool g[501][501]; 65 inline int gg(){ 66 // freopen("walk.in","r",stdin); 67 // freopen("walk.out","w",stdout); 68 n=read(),m=read(); 69 for(int i=1;i<=m;i++){ 70 int x(read()),y(read()); 71 insert(x,y); 72 g[x][y]=g[y][x]=1; 73 du[x]++,du[y]++; 74 } 75 du[n]=0; 76 for(int i=1;i<=n;i++){ 77 if(i==1) 78 b[i]=1; 79 else 80 b[i]=0; 81 for(int j=1;j<=n;j++){ 82 if(i==j){ 83 a[i][j]=1; 84 continue; 85 } 86 if(j==n){ 87 a[i][j]=0; 88 continue; 89 } 90 if(g[i][j]) 91 a[i][j]=-1.0/(double)du[j]; 92 } 93 } 94 gauss(); 95 for(int i=1;i<n;i++) 96 ans[i]/=du[i]; 97 ans[n]=0; 98 for(int i=1;i<=tot;i++){ 99 int s(ed[i].s),e(ed[i].e); 100 f[i]=ans[s]+ans[e]; 101 } 102 int cnt(tot); 103 sort(f+1,f+tot+1); 104 double an(0); 105 for(int i=1;i<=tot;i++) 106 an+=i*f[cnt--]; 107 printf("%.3lf",an); 108 return 0; 109 } 110 int k(gg()); 111 int main(){;}
ps:COGS rk1代码奉上,虽然榜貌似被两个神奇的(hhh)刷成(hhh)了,但是还是没有什么影响。。。