高斯消元解线性期望方程的妙用

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
",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;
}

  

原文地址:https://www.cnblogs.com/Stump/p/8418177.html