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<