bzoj4637 矩阵树定理

long double的输出方式真的是个迷啊。

矩阵树定理:

设矩阵A=度数矩阵-邻接矩阵。

无向图的生成树个数就是A的n-1阶主子式的值。

有向图。。

外向树:A=入度矩阵-邻接矩阵。

内向树:A=出度矩阵-邻接矩阵。答案是A去掉根所在的一行一列的n-1阶主子式的值。

证明:留坑。。

关于此题:

由于期望的线性性,ans=sum(每条边的权值*它出现在最小生成树中的概率)。

出现在最小生成树中的概率。。。根据kruskal的过程,可以发现那些权值不一样的边怎么选对它的概率一点影响都没有。。。

而且每种权值的边做完以后图的连通性一定是一样的。于是对于同种权值的边,就可以在做的时候求出做完后每个连通块的生成树个数。。。再求出去掉任意一条边后的生成树个数。。。可以得到概率。然后由于同种权值的边不超过30条,做生成树时涉及到的点也就不超过31啊qwq,然后优化一下就可以了qwq。(中毒了。。)

具体不说了。。。自己yy一下感觉还是比较显然的。。关键是期望的线性性啊线性性啊QAQ。。。

没有被卡精度非常开心,但是写题的时候i j打反以至于WA了5遍数组没清空又WA5遍真的是惨痛的经历啊。。。此题的对拍又很难造数据以至于我很久以后才发现错误啊。。。(菜)QAQ。所以说一定要好好检查代码呀。。肉眼对拍不能菜到我这种ij打反都看不出来的地步呀。。。(正确率又掉了。。。QAQ)都在口胡些什么

#include
using namespace std;
#define rep(x,y,z) for (int x=y; x<=z; x++)
#define downrep(x,y,z) for (int x=y; x>=z; x--)
#define ms(x,y,z) memset(x,y,sizeof(z))
#define LL long long
#define repedge(x,y) for (int x=hed[y]; ~x; x=edge[x].nex)
inline LL read(){
    LL x=0; int w=0; char ch=0;
    while (ch<'0' || ch>'9') w|=ch=='-',ch=getchar();
    while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return w? -x:x;
}
const int maxn=65;
const int N=100005;
const int M=200005; 
const long double eps=1e-9;
int n,m,vis[N],tmp[N],f[N],bef[N],pd[N],vec[maxn][maxn];
long double ans;
struct node{ int a,b,dis,val; }e[M],E[M];
bool operator < (node a,node b){
    return (a.disfabs(a.num[r][i])) r=j;
        rep(j,i,n) swap(a.num[i][j],a.num[r][j]);
        if (zero(a.num[i][i])) return 0;
        if (r!=i) w*=(-1);
        rep(j,i+1,n)if (a.num[j][i]){ 
           long double f=a.num[j][i]/a.num[i][i];
           rep(k,i,n) a.num[j][k]-=f*a.num[i][k];
        }
    }
    rep(i,1,n) w*=a.num[i][i]; return w;
}
void kruskal(){
    sort(e+1,e+m+1); 
	rep(i,1,n) f[i]=i; int r; int sum=0; int cnt=0;
    for(int i=1; i<=m; i=r+1){
        for(r=i; r<=m; r++) if (e[r].dis!=e[i].dis) break; r--; 
		for(int j=i; j<=r; j++){
        	int fa=gf(e[j].a); int fb=gf(e[j].b);
			E[j].a=fa; E[j].b=fb;
		}
		for(int j=i; j<=r; j++){
			int fa=gf(e[j].a); int fb=gf(e[j].b);
			if (fa!=fb) f[fa]=fb;
		}
		for(int j=1; j<=sum; j++) vec[j][0]=0;
		for(int j=1; j<=sum; j++) pd[bef[j]]=0; sum=0;
		for(int j=i; j<=r; j++){
			int fa=gf(e[j].a); 
			if (!pd[fa]){ bef[++sum]=fa; pd[fa]=sum; }
			if (E[j].a!=E[j].b) vec[pd[fa]][++vec[pd[fa]][0]]=j;
		}
		for(int j=1; j<=sum; j++){
			for(int k=1; k<=cnt; k++) for(int p=1; p<=cnt; p++) b.num[k][p]=0;
			for(int k=1; k<=cnt; k++) vis[tmp[k]]=0; cnt=0;
            for(int k=1; k<=vec[j][0]; k++){
            	int fa=E[vec[j][k]].a; int fb=E[vec[j][k]].b;
            	if (!vis[fa]){ tmp[++cnt]=fa; vis[fa]=cnt; }
            	if (!vis[fb]){ tmp[++cnt]=fb; vis[fb]=cnt; }
            	addedge(vis[fa],vis[fb]);
			}if (!cnt) continue;
			long double tot=det(cnt-1); if (zero(tot)) continue;
			for(int k=1; k<=vec[j][0]; k++){
				int fa=vis[E[vec[j][k]].a]; int fb=vis[E[vec[j][k]].b];
				moveedge(fa,fb); long double x=tot-det(cnt-1);   
				ans+=x/tot*e[vec[j][k]].val; addedge(fa,fb);
			}
		}
    }
}
int main(){
    n=read(); m=read();
    rep(i,1,m) e[i].a=read(),e[i].b=read(),e[i].dis=read(),e[i].val=read();
    ans=0; kruskal(); 
    cout<

 

你可能感兴趣的:(数学)