P3232 [HNOI2013]游走
吐槽
傻了傻了,对着题解改了好长时间最后发现是自己忘了调用高斯消元了。。。
思路
期望题,分配编号,显然编号大的分给贡献次数小的,所以需要知道每个边被经过次数的期望
然后边被经过的次数的期望就是连接的两个点到它的次数的期望求和,点走哪条边次数的期望相等(都是1/度数)
然后就是求每个点的次数的期望,正推,高斯消元
注意因为计算的是次数的期望,所以f1要加上1(开局经过一次)且所有的点都不能从fn转移来(因为到n就结束了,所以消元的时候也不用管fn
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
#define double long double
using namespace std;
const double eps=1e-9;
int n,m,u[1010000],v[1010000],fir[1010],nxt[1010000],d[1010],cnt;
double a[510][510],ans=0,Ex[1010000];
struct Edge{
int u,v;
}E[1010000];
void gauss(void){
for(int i=1;i<n;i++){
for(int j=i;j<n;j++){
if(fabs(a[j][i])>eps){
for(int k=1;k<=n;k++)
swap(a[i][k],a[j][k]);
break;
}
}
for(int j=1;j<n;j++){
if(j==i)
continue;
double rate=a[j][i]/a[i][i];
for(int k=i;k<=n;k++)
a[j][k]=a[j][k]-rate*a[i][k];
}
}
for(int i=1;i<n;i++)
a[i][n]=a[i][n]/a[i][i];
return;
}
void addedge(int ui,int vi){
++cnt;
u[cnt]=ui;
v[cnt]=vi;
d[ui]++;
nxt[cnt]=fir[ui];
fir[ui]=cnt;
}
void build(void){
// a[n][n]=1;
a[1][n]=1;
for(int i=1;i<n;i++){
a[i][i]+=1;
// a[i][n+1]+=1;
for(int j=fir[i];j;j=nxt[j]){
if(v[j]!=n)
a[i][v[j]]-=1.0/d[v[j]];
}
}
}
signed main(){
scanf("%lld %lld",&n,&m);
for(int i=1;i<=m;i++){
int a,b;
scanf("%lld %lld",&a,&b);
addedge(a,b);
addedge(b,a);
E[i].u=a;
E[i].v=b;
}
build();
gauss();
for(int i=1;i<=m;i++){
Ex[i]+=a[E[i].u][n]/d[E[i].u];
Ex[i]+=a[E[i].v][n]/d[E[i].v];
}
sort(Ex+1,Ex+m+1);
for(int i=1,j=m;i<=m;i++,j--)
ans=ans+j*Ex[i]*1.0;
printf("%.3Lf\n",ans);
return 0;
}