分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 前端开发

P4208 [JSOI2008]最小生成树计数

发布时间:2023-09-06 02:15责任编辑:白小东关键词:暂无标签

P4208 [JSOI2008]最小生成树计数

矩阵树定理+最小生成树

神犇的题解

↑↑需要的2个定理

根据定理,我们需要求出的是每层相同权值生成树方案之积

所以在最小生成树求解过程中嵌入计算过程:每次建一个新图,计算新图行列式的值。

因为模数不是质数所以高斯消元就用辗转相除了

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cctype>using namespace std;template <typename T> inline void read(T &x){ ???char c=getchar(); x=0; ???while(!isdigit(c)) c=getchar(); ???while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();}const int mod=31011;struct edge{int from,to,dis;}a[1002],c[1002];inline bool cmp(const edge &A,const edge &B) {return A.dis<B.dis;}int n,m,ans=1,k,fa1[102],fa2[102],f[102][102],b[102]; bool vis[102];inline int find1(int x){return fa1[x]==x ? x:fa1[x]=find1(fa1[x]);}inline int find2(int x){return fa2[x]==x ? x:fa2[x]=find2(fa2[x]);}inline int det(int t){ ???int res=1; ???for(int i=1;i<t;++i){ ???????for(int j=i+1;j<t;++j){ ???????????while(f[j][i]){ //辗转相除 ???????????????int div=f[i][i]/f[j][i]; ???????????????for(int k=i;k<t;++k) f[i][k]=(f[i][k]-1LL*f[j][k]*div%mod+mod)%mod; ???????????????swap(f[i],f[j]); res=-res; ???????????} ???????} ???????res=(res+mod)%mod*f[i][i]%mod; ???}return (res+mod)%mod;}inline void mt(int st,int ed){ ???memset(vis,0,sizeof(vis)); ???memset(f,0,sizeof(f)); ???for(int i=st;i<=ed;++i){ ???????c[i]=((edge){find1(a[i].from),find1(a[i].to),a[i].dis}); //新边,编号为原连通块的编号 ???????if(c[i].from==c[i].to) continue; ???????if(!vis[c[i].from]) vis[c[i].from]=1; ???????if(!vis[c[i].to]) vis[c[i].to]=1; ???}int cnt=0; ???for(int i=1;i<=n;++i) if(vis[i]) b[++cnt]=i,fa2[cnt]=cnt; //新点 ???for(int i=st;i<=ed;++i){ ???????if(c[i].from==c[i].to) continue; ???????int r1=find1(c[i].from),r2=find1(c[i].to); ???????if(r1!=r2) fa1[r1]=r2,++k; ???????int u=lower_bound(b+1,b+cnt+1,c[i].from)-b; ???????int v=lower_bound(b+1,b+cnt+1,c[i].to)-b; ???????++f[u][u];++f[v][v]; ???????f[u][v]=(f[u][v]-1+mod)%mod; //每次减法取模 ???????f[v][u]=(f[v][u]-1+mod)%mod; ???????r1=find2(u),r2=find2(v); ???????if(r1!=r2) fa2[r1]=r2; ???} ???for(int i=1;i<cnt;++i){ //为了防止新图不连通,在每个连通块间加入一条唯一任意边。这并不会影响生成树方案数 ???????int r1=find2(i),r2=find2(i+1); ???????if(r1==r2) continue; ???????fa2[r1]=r2; ++f[r1][r1];++f[r2][r2]; ???????f[r1][r2]=(f[r1][r2]-1+mod)%mod; ???????f[r2][r1]=(f[r2][r1]-1+mod)%mod; ???}ans=1LL*ans*det(cnt)%mod;}int main(){ ???read(n); read(m); ???for(int i=1;i<=n;++i) fa1[i]=i; ???for(int i=1;i<=m;++i) read(a[i].from),read(a[i].to),read(a[i].dis); ???sort(a+1,a+m+1,cmp); ???for(int i=1,j;i<=m&&k<n-1;i=j){ ???????for(j=i+1;j<=m;++j) if(a[i].dis!=a[j].dis) break; ???????if(j-i>1) {mt(i,j-1); continue;} //相同权值的边超过1条就要计算生成树的方案了 ???????int r1=find1(a[i].from),r2=find1(a[i].to); ???????if(r1!=r2) fa1[r1]=r2,++k; ???}if(k<n-1) ans=0; ???printf("%d",ans); ???return 0;}

P4208 [JSOI2008]最小生成树计数

原文地址:https://www.cnblogs.com/kafuuchino/p/9671240.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved