最小生成树计数

Minimum Spanning Tree http://acm.hdu.edu.cn/showproblem.php?pid=4408

模板题

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<algorithm>
  5 #define mt(a,b) memset(a,b,sizeof(a))
  6 using namespace std;
  7 typedef __int64 LL;
  8 class MinST_count { ///最小生成树计数
  9     typedef int typec;///边权的类型
 10     static const int ME=1024;///边的个数
 11     static const int MV=128;///点的个数
 12     struct E {
 13         int u,v;
 14         typec w;
 15         friend bool operator <(E a,E b) {
 16             return a.w<b.w;
 17         }
 18     } e[ME];
 19     LL mod,ans,mat[MV][MV];
 20     int n,le,fa[MV],ka[MV],gk[MV][MV];
 21     bool vis[MV];
 22     vector<int> gra[MV];
 23     int getroot(int a,int b[]) {
 24         return a==b[a]?a:b[a]=getroot(b[a],b);
 25     }
 26     LL det(LL a[][MV],int n) { ///生成树计数
 27         for(int i=0; i<n; i++)
 28             for(int j=0; j<n; j++)
 29                 a[i][j]%=mod;
 30         LL ret=1;
 31         for(int i=1; i<n; i++) {
 32             for(int j=i+1; j<n; j++) {
 33                 while(a[j][i]) {
 34                     LL t=a[i][i]/a[j][i];
 35                     for(int k=i; k<n; k++)
 36                         a[i][k]=(a[i][k]-a[j][k]*t)%mod;
 37                     for(int k=i; k<n; k++)
 38                         swap(a[i][k],a[j][k]);
 39                     ret=-ret;
 40                 }
 41             }
 42             if(!a[i][i]) return 0;
 43             ret=ret*a[i][i]%mod;
 44         }
 45         return (ret+mod)%mod;
 46     }
 47 public:
 48     void init(int tn,int tmod) { ///传入点的个数,%tmod,点下标1开始
 49         n=tn;
 50         mod=tmod;
 51         le=0;
 52         mt(fa,0);
 53         mt(ka,0);
 54         mt(vis,0);
 55         mt(gk,0);
 56         mt(mat,0);
 57     }
 58     void add(int u,int v,typec w) {
 59         e[le].u=u;
 60         e[le].v=v;
 61         e[le].w=w;
 62         le++;
 63     }
 64     LL solve() {///返回生成树个数%mod
 65         sort(e,e+le);
 66         for(int i=1; i<=n; i++){
 67             fa[i]=i;
 68             vis[i]=false;
 69             gra[i].clear();
 70         }
 71         typec pre=-1;
 72         ans=1;
 73         for(int h=0; h<=le; h++) {
 74             if(e[h].w!=pre||h==le) { ///一组边加完
 75                 for(int i=1; i<=n; i++) {
 76                     if(vis[i]) {
 77                         int u=getroot(i,ka);
 78                         gra[u].push_back(i);
 79                         vis[i]=false;
 80                     }
 81                 }
 82                 for(int i=1; i<=n; i++) { ///枚举每个联通分量
 83                     if(gra[i].size()>1) {
 84                         mt(mat,0);
 85                         int len=gra[i].size();
 86                         for(int a=0; a<len; a++) { ///构建矩阵
 87                             for(int b=a+1; b<len; b++) {
 88                                 LL la=gra[i][a],lb=gra[i][b];
 89                                 mat[a][b]=(mat[b][a]-=gk[la][lb]);
 90                                 mat[a][a]+=gk[la][lb];
 91                                 mat[b][b]+=gk[la][lb];
 92                             }
 93                         }
 94                         LL ret=(LL)det(mat,len);
 95                         ans=(ans*ret)%mod;
 96                         for(int a=0; a<len; a++)
 97                             fa[gra[i][a]]=i;
 98                     }
 99                 }
100                 for(int i=1; i<=n; i++) {
101                     ka[i]=fa[i]=getroot(i,fa);
102                     gra[i].clear();
103                 }
104                 if(h==le)break;
105                 pre=e[h].w;
106             }
107             int a=e[h].u;
108             int b=e[h].v;
109             int pa=getroot(a,fa);
110             int pb=getroot(b,fa);
111             if(pa==pb)continue;
112             vis[pa]=vis[pb]=true;
113             ka[getroot(pa,ka)]=getroot(pb,ka);
114             gk[pa][pb]++;
115             gk[pb][pa]++;
116         }
117         for(int i=2; i<=n&&ans; i++)
118             if(ka[i]!=ka[i-1])
119                 ans=0;
120         ans=(ans+mod)%mod;
121         return ans;
122     }
123 }gx;
124 int main() {
125     int n,m,p;
126     while(~scanf("%d%d%d",&n,&m,&p),n|m|p) {
127         gx.init(n,p);
128         while(m--) {
129             int u,v,w;
130             scanf("%d%d%d",&u,&v,&w);
131             gx.add(u,v,w);
132         }
133         printf("%I64d\n",gx.solve());
134     }
135     return 0;
136 }

1016: [JSOI2008]最小生成树计数 http://www.lydsy.com/JudgeOnline/problem.php?id=1016

一样

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<algorithm>
  5 #define mt(a,b) memset(a,b,sizeof(a))
  6 using namespace std;
  7 typedef long long LL;
  8 class MinST_count { ///最小生成树计数
  9     typedef int typec;///边权的类型
 10     static const int ME=1024;///边的个数
 11     static const int MV=128;///点的个数
 12     struct E {
 13         int u,v;
 14         typec w;
 15         friend bool operator <(E a,E b) {
 16             return a.w<b.w;
 17         }
 18     } e[ME];
 19     LL mod,ans,mat[MV][MV];
 20     int n,le,fa[MV],ka[MV],gk[MV][MV];
 21     bool vis[MV];
 22     vector<int> gra[MV];
 23     int getroot(int a,int b[]) {
 24         return a==b[a]?a:b[a]=getroot(b[a],b);
 25     }
 26     LL det(LL a[][MV],int n) { ///生成树计数
 27         for(int i=0; i<n; i++)
 28             for(int j=0; j<n; j++)
 29                 a[i][j]%=mod;
 30         LL ret=1;
 31         for(int i=1; i<n; i++) {
 32             for(int j=i+1; j<n; j++) {
 33                 while(a[j][i]) {
 34                     LL t=a[i][i]/a[j][i];
 35                     for(int k=i; k<n; k++)
 36                         a[i][k]=(a[i][k]-a[j][k]*t)%mod;
 37                     for(int k=i; k<n; k++)
 38                         swap(a[i][k],a[j][k]);
 39                     ret=-ret;
 40                 }
 41             }
 42             if(!a[i][i]) return 0;
 43             ret=ret*a[i][i]%mod;
 44         }
 45         return (ret+mod)%mod;
 46     }
 47 public:
 48     void init(int tn,int tmod) { ///传入点的个数,%tmod,点下标1开始
 49         n=tn;
 50         mod=tmod;
 51         le=0;
 52         mt(fa,0);
 53         mt(ka,0);
 54         mt(vis,0);
 55         mt(gk,0);
 56         mt(mat,0);
 57     }
 58     void add(int u,int v,typec w) {
 59         e[le].u=u;
 60         e[le].v=v;
 61         e[le].w=w;
 62         le++;
 63     }
 64     LL solve() {///返回生成树个数%mod
 65         sort(e,e+le);
 66         for(int i=1; i<=n; i++){
 67             fa[i]=i;
 68             vis[i]=false;
 69             gra[i].clear();
 70         }
 71         typec pre=-1;
 72         ans=1;
 73         for(int h=0; h<=le; h++) {
 74             if(e[h].w!=pre||h==le) { ///一组边加完
 75                 for(int i=1; i<=n; i++) {
 76                     if(vis[i]) {
 77                         int u=getroot(i,ka);
 78                         gra[u].push_back(i);
 79                         vis[i]=false;
 80                     }
 81                 }
 82                 for(int i=1; i<=n; i++) { ///枚举每个联通分量
 83                     if(gra[i].size()>1) {
 84                         mt(mat,0);
 85                         int len=gra[i].size();
 86                         for(int a=0; a<len; a++) { ///构建矩阵
 87                             for(int b=a+1; b<len; b++) {
 88                                 LL la=gra[i][a],lb=gra[i][b];
 89                                 mat[a][b]=(mat[b][a]-=gk[la][lb]);
 90                                 mat[a][a]+=gk[la][lb];
 91                                 mat[b][b]+=gk[la][lb];
 92                             }
 93                         }
 94                         LL ret=(LL)det(mat,len);
 95                         ans=(ans*ret)%mod;
 96                         for(int a=0; a<len; a++)
 97                             fa[gra[i][a]]=i;
 98                     }
 99                 }
100                 for(int i=1; i<=n; i++) {
101                     ka[i]=fa[i]=getroot(i,fa);
102                     gra[i].clear();
103                 }
104                 if(h==le)break;
105                 pre=e[h].w;
106             }
107             int a=e[h].u;
108             int b=e[h].v;
109             int pa=getroot(a,fa);
110             int pb=getroot(b,fa);
111             if(pa==pb)continue;
112             vis[pa]=vis[pb]=true;
113             ka[getroot(pa,ka)]=getroot(pb,ka);
114             gk[pa][pb]++;
115             gk[pb][pa]++;
116         }
117         for(int i=2; i<=n&&ans; i++)
118             if(ka[i]!=ka[i-1])
119                 ans=0;
120         ans=(ans+mod)%mod;
121         return ans;
122     }
123 }gx;
124 int main() {
125     int n,m,p;
126     while(~scanf("%d%d",&n,&m)) {
127         gx.init(n,31011);
128         while(m--) {
129             int u,v,w;
130             scanf("%d%d%d",&u,&v,&w);
131             gx.add(u,v,w);
132         }
133         printf("%lld\n",gx.solve());
134     }
135     return 0;
136 }

end

时间: 2024-09-29 19:33:21

最小生成树计数的相关文章

BZOJ_1016_[JSOI2008]_最小生成树计数_(dfs+乘法原理)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1016 给出一张图,其中具有相同权值的边的数目不超过10,求最小生成树的个数. 分析 生成树的计数有一个什么什么算法... 我真的企图研究了...但是智商捉急的我实在看不懂论文... 所以最后还是写了暴力... 当然暴力也要靠正确的姿势的. 首先来看一个结论: 同一张图的所有最小生成树中,边权值相同的边的数目是一定的. 也就是说,假如某一张图的某一棵最小生成树由边权值为1,1,2,2,2,3的

【BZOJ】【1016】【JSOI2008】最小生成树计数

Kruskal/并查集+枚举 唉我还是too naive,orz Hzwer 一开始我是想:最小生成树删掉一条边,再加上一条边仍是最小生成树,那么这两条边权值必须相等,但我也可以去掉两条权值为1和3的,再加上权值为2和2的,不也满足题意吗?事实上,如果这样的话……最小生成树应该是1和2,而不是1和3或2和2!!! 所以呢?所以对于一个图来说,最小生成树有几条边权为多少的边,都是固定的!所以我们可以做一遍Kruskal找出这些边权,以及每种边权出现的次数.然后,对于每种边权,比方说出现了$v_i$

JSOI2008 最小生成树计数

题解: 最小生成树的两个性质: 1.边权相等的边的个数一定. 2.做完边权为w的所有边时,图的连通性相同. 证明: 1.边权相等的边的个数不一样的话就不会都同时是最小生成树了. 2.假设每种方法的做完边权为w的连通性不同,那么假设i边和j边没有同时被选,那么我们完全可以在一种方案中加入i边(或j边),使得连通性增强,而后面费用更大的边用的更少,这样与这是最小生成树矛盾.于是,命题得证. 代码:不知为何,下面程序有bug,什么时候再回来A掉…… 1 type node1=record 2 x,y,

【bzoj1016】 JSOI2008—最小生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1016 (题目链接) 题意:求图的最小生成树计数. Solution  %了下题解,发现要写矩阵树,150++的程序什么鬼.于是就蒯了hzwer的简便方法.  将边按照权值大小排序,将权值相同的边分到一组,统计下每组分别用了多少条边.然后对于每一组进行dfs,判断是否能够用这一组中的其他边达到相同的效果.最后把每一组的方案数相乘就是答案.  注意并查集不要压缩路径,不然的话不好回溯. 代码: //

BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3569  Solved: 1425 [Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对3101

1016: [JSOI2008]最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的 最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生 成树可能很多,所以你只需要输出方案数对3101

[JSOI2008][BZOJ1016] 最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3379  Solved: 1336[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的

【BZOJ】1016: [JSOI2008]最小生成树计数 深搜+并查集

最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树 可能很多,所以你只需要输出方案数对31011的模就可以了. Input 第 一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数.每个节点用1~n的整数编号.接下来的m行,每行包含两个整数:a,

【JSOI2008】【BZOJ1016】最小生成树计数

我就爱写矩阵树定理!!! 就不写暴力!!! 1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec Memory Limit: 162 MB Submit: 3584 Solved: 1429 [Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可