BZOJ3143
http://www.lydsy.com/JudgeOnline/problem.php?id=3143
Ei表示经i点的期望
E(u,v)表示经(u,v)的期望
特别地有
构造线性期望方程,高斯消元即可.
根据排序不等式,贪心即可
#include<cstdio>
#include<algorithm>
using namespace std;
double a[511][511],x[511],w[250011],ans;
int deg[511];
int u[250011],v[250011];
int n,m;
inline void init(){
for(register int i=1;i<=m;++i){
a[u[i]][v[i]]+=1.00/deg[v[i]];
a[v[i]][u[i]]+=1.00/deg[u[i]];
}
for(register int i=1;i<n;++i)
a[i][i]=-1.00;
a[1][n+1]=-1.00;
for(register int i=1;i<=n+1;++i)
a[n][i]=0.00;
a[n][n]=1.00;
}
inline void gauss(){
double tmp;
for(register int i=1,p;i<=n;++i)
for(register int j=i+1;j<=n;++j){
tmp=1.00*a[j][i]/a[i][i];
for(register int k=i;k<=n+1;++k)
a[j][k]-=1.00*a[i][k]*tmp;
}
for(register int i=n;i;--i){
for(register int j=i+1;j<=n;++j)a[i][n+1]-=x[j]*a[i][j];
x[i]=a[i][n+1]/a[i][i];
}
}
inline bool cmp(double a,double b){
return a>b;
}
int main(){
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;++i){
scanf("%d%d",v+i,u+i);
++deg[v[i]];++deg[u[i]];
}
init();
gauss();
for(register int i=1;i<=m;++i)w[i]=1.00*x[u[i]]/(1.00*deg[u[i]])+1.00*x[v[i]]/(1.00*deg[v[i]]);
sort(w+1,w+m+1,cmp);
for(register int i=1;i<=m;++i)
ans+=1.00*i*w[i];
printf("%.3lf",ans);
return 0;
}
BZOJ2337
http://www.lydsy.com/JudgeOnline/problem.php?id=2337
按二进制枚举每位,然后做法类似与上面
Ei表示i到n的二进制第k位为1的期望
对于每次消元后对E1*2k求个和
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1e6+7;
double a[501][501],_x[501],ans;
int nxt[maxn],las[maxn],to[maxn],w[maxn],deg[maxn];
int n,m,x,y,z,tot;
inline void add(int x,int y,int z){
nxt[++tot]=las[x];
las[x]=tot;
to[tot]=y;
w[tot]=z;
++deg[y];
}
inline void gauss(){
double tmp;
for(register int i=1,p;i<=n;++i){
p=i;
while(!a[p][i])++p;
if(p!=i)swap(a[p],a[i]);
for(register int j=i+1;j<=n;++j){
tmp=a[j][i]/a[i][i];
for(register int k=i;k<=n+1;++k)
a[j][k]-=1.00*a[i][k]*tmp;
}
}
for(register int i=n;i;--i){
for(register int j=i+1;j<=n;++j)
a[i][n+1]-=1.00*_x[j]*a[i][j];
_x[i]=a[i][n+1]/a[i][i];
}
}
int main(){
scanf("%d%d",&n,&m);
for(register int i=1;i<=m;++i){
scanf("%d%d%d",&x,&y,&z);
if(x!=y)add(x,y,z),add(y,x,z);
else add(x,y,z);
}
for(register int i=0;i<=30;++i){
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n+1;++j)
a[i][j]=0.00;
for(register int j=1;j<n;++j){
a[j][j]=1.00;
for(register int e=las[j];e;e=nxt[e]){
x=to[e];
if((w[e]>>i)&1){
a[j][x]+=1.00/deg[j];
a[j][n+1]+=1.00/deg[j];
}
else
a[j][x]-=1.00/deg[j];
}
}
a[n][n]=1.00;
gauss();
ans+=1.00*_x[1]*(1<<i);
}
printf("%.3lf\n",ans);
return 0;
}
BZOJ3534
http://www.lydsy.com/JudgeOnline/problem.php?id=3534
就是求这一坨,新边权为
结合矩阵树定理,构造行列式,做高斯消元,最后乘上后面那一坨就好了
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod=998244353;
typedef double db;
db a[5001][5001],ans;
int x,y,n,m;
inline db fabs(db x){
return x>=0?x:-x;
}
inline db gauss(){
db tmp;
for(register int i=1,t;i<n;++i){
t=i;
for(register int j=i+1;j<n;++j)
if(fabs(a[t][i])<fabs(a[j][i]))
t=j;
if(!a[t][i])return 0.00;
swap(a[t],a[i]);
for(register int j=i+1;j<n;++j){
tmp=1.00*a[j][i]/a[i][i];
for(register int k=i;k<n;++k)
a[j][k]-=1.00*a[i][k]*tmp;
}
}
tmp=1.00;
for(register int i=1;i<n;++i)
tmp=tmp*a[i][i];
return fabs(tmp);
}
int main(){
scanf("%d",&n);
ans=1;
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j){
scanf("%lf",&a[i][j]);
if(i==j)continue;
if(i<j)ans=ans*(1.00-a[i][j]);
a[i][j]=a[i][j]/(1.00-a[i][j]);
}
for(register int i=1;i<=n;++i)
for(register int j=1;j<=n;++j)
if(i!=j)
a[i][i]-=a[i][j];
ans=ans*gauss();
printf("%.8lf",ans);
return 0;
}