bzoj1016

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

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 3517  Solved: 1396
[Submit][Status][Discuss]

Description

现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。

Input

第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。

Output

输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

Sample Input

4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1

Sample Output

8

HINT

Source

题意:问一个图的最小生成树有多少种

分析:

下面提供两种做法:

1、最小生成树+暴力枚举边

现象普通最小生成树一样,将边按权值排序,先做一遍最小生成树(Krus什么的),统计每种权值取得边数

显然,每种权值的边取的数量是一定的(根据最小生成树的性质,先假设前面权值的边已经取好,那现在这个权值的边每取一条,都会减少一个联通块,这样想,显然数量一定)

这样,对于每种权值的边暴力枚举每条边取不取,在尝试加入,若能加入,则这种权值的边的取法+1,最后把每种权值的边的取法乘起来

2、利用每种权值的边取的数量是一定的性质,很容易推出无论怎么加,加完某种权值的边后,图的连通性是一样的,那么,对于每种那些加完边后变成一个联通块的若干联通块,就相当于一个生成树,那么很显然可以用生成树计数

何为生成树计数(这么多天不添题就是搞这个去了,终于搞懂了)

虽然我搞懂了,但我也是看论文看懂的,有想学的自己看论文去吧

大致意思如下:

定义度数矩阵D:D[i][i]为第 i 个点的度数

定义边矩阵E:E[i][j] 为第 i 个点到第 j 个点的边的数量(边的数量也是成立的,当初我就是纠结这个,要完全搞懂生成树计数的原理才能懂哦)

定义矩阵C:C[i][j] = D[i][j] - E[i][j]

那么,生成树的数量就是矩阵C的任意一个n-1阶的主子树的行列式

行列式有一下性质:

1、将一行数每一个数乘以某一个数,加到令一行数上,行列式的结果不变

2、将任意两行调转,结果相反(符号取反)

3、上三角或下三角的行列式值为对角线的乘积

何为上三角、下三角

a1 a2 .... an

0   b2......bn

0    0  c3....cn

0 0 0 0........pn

即对角线的一边有数,另一边没数

PS:对于第二种做法,一定要判断能不能得到一棵树,不能的话,就输出 0 (卡了我好久T_T)

综上所述,本题得解

第一种

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <deque>
  6 #include <vector>
  7 #include <queue>
  8 #include <iostream>
  9 #include <algorithm>
 10 #include <map>
 11 #include <set>
 12 #include <ctime>
 13 using namespace std;
 14 typedef long long LL;
 15 typedef double DB;
 16 #define For(i, s, t) for(int i = (s); i <= (t); i++)
 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--)
 18 #define MIT (2147483647)
 19 #define INF (1000000001)
 20 #define MLL (1000000000000000001LL)
 21 #define sz(x) ((int) (x).size())
 22 #define clr(x, y) memset(x, y, sizeof(x))
 23 #define puf push_front
 24 #define pub push_back
 25 #define pof pop_front
 26 #define pob pop_back
 27 #define ft first
 28 #define sd second
 29 #define mk make_pair
 30 inline void SetIO(string Name) {
 31     string Input = Name+".in",
 32     Output = Name+".out";
 33     freopen(Input.c_str(), "r", stdin),
 34     freopen(Output.c_str(), "w", stdout);
 35 }
 36
 37 const int N = 110, M = 1010, Mod = 31011;
 38 struct Edges {
 39     int u, v, c;
 40
 41     inline bool operator <(const Edges &A) const {
 42         return c < A.c;
 43     }
 44 } E[M];
 45 int n, m;
 46 int Fa[N], U[N], Stack[N];
 47 int Step[M], S, St[M], Ed[M];
 48 int Ans;
 49
 50 inline void Input() {
 51     scanf("%d%d", &n, &m);
 52     For(i, 1, m)
 53         scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].c);
 54 }
 55
 56 inline int Find(int x, int *P) {
 57     int Len = 0;
 58     while(x != P[x]) {
 59         Stack[++Len] = x;
 60         x = P[x];
 61     }
 62     For(i, 1, Len) P[Stack[i]] = x;
 63     return x;
 64 }
 65
 66 inline int Count(int State) {
 67     int Ret = 0;
 68     while(State) {
 69         if(State&1) Ret++;
 70         State >>= 1;
 71     }
 72     return Ret;
 73 }
 74
 75 inline void Solve() {
 76     sort(E+1, E+1+m);
 77     For(i, 1, n) Fa[i] = i;
 78
 79     S = 0;
 80     int Last = -1;
 81     For(i, 1, m) {
 82         if(Last != E[i].c) {
 83             Ed[S] = i-1;
 84             S++, Last = E[i].c;
 85             St[S] = i;
 86         }
 87         int u = Find(E[i].u, Fa), v = Find(E[i].v, Fa);
 88         if(u == v) continue;
 89         Fa[u] = v, Step[S]++;
 90     }
 91     Ed[S] = m;
 92
 93     Ans = 1;
 94     For(i, 1, n) Fa[i] = i;
 95     int Cnt, Len, Max;
 96     bool Flag;
 97     For(k, 1, S) {
 98         if(!Step[k]) {
 99             //printf("0\n");
100             continue;
101         }
102         Cnt = 0, Len = Ed[k]-St[k]+1;
103         Max = 1<<Len;
104         For(State, 0, Max-1) {
105             if(Count(State) != Step[k]) continue;
106             For(i, 1, n) U[i] = Fa[i];
107             Flag = 1;
108             For(i, 0, Len-1)
109                 if((1<<i)&State) {
110                     int u = Find(E[St[k]+i].u, U),
111                         v = Find(E[St[k]+i].v, U);
112                     if(u == v) {
113                         Flag = 0;
114                         break;
115                     }
116                     U[u] = v;
117                 }
118             if(Flag) Cnt++;
119         }
120
121         For(i, St[k], Ed[k]) {
122             int u = Find(E[i].u, Fa),
123                 v = Find(E[i].v, Fa);
124             if(u == v) continue;
125             Fa[u] = v;
126         }
127
128         //printf("%d\n", Cnt);
129         Ans = (Ans*Cnt)%Mod;
130     }
131
132     For(i, 1, n) Fa[i] = Find(i, Fa);
133     For(i, 2, n)
134         if(Find(i, Fa) != Find(i-1, Fa)) {
135             Ans = 0;
136             break;
137         }
138     printf("%d\n", Ans);
139 }
140
141 int main() {
142     SetIO("1016");
143     Input();
144     Solve();
145     return 0;
146 }

第二种

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <cmath>
  5 #include <deque>
  6 #include <vector>
  7 #include <queue>
  8 #include <iostream>
  9 #include <algorithm>
 10 #include <map>
 11 #include <set>
 12 #include <ctime>
 13 using namespace std;
 14 typedef long long LL;
 15 typedef double DB;
 16 #define For(i, s, t) for(int i = (s); i <= (t); i++)
 17 #define Ford(i, s, t) for(int i = (s); i >= (t); i--)
 18 #define MIT (2147483647)
 19 #define INF (1000000001)
 20 #define MLL (1000000000000000001LL)
 21 #define sz(x) ((int) (x).size())
 22 #define clr(x, y) memset(x, y, sizeof(x))
 23 #define puf push_front
 24 #define pub push_back
 25 #define pof pop_front
 26 #define pob pop_back
 27 #define ft first
 28 #define sd second
 29 #define mk make_pair
 30 inline void SetIO(string Name) {
 31     string Input = Name+".in",
 32     Output = Name+".out";
 33     freopen(Input.c_str(), "r", stdin),
 34     freopen(Output.c_str(), "w", stdout);
 35 }
 36
 37 const int N = 110, M = 1010, Mod = 31011;
 38 struct Edges {
 39     int u, v, c;
 40
 41     inline bool operator <(const Edges &A) const {
 42         return c < A.c;
 43     }
 44 } E[M];
 45 int n, m;
 46 int Fa[N], Stack[N];
 47 int Edge[N][N], Con[N][N], Len[N];
 48 int C[N][N];
 49 int S, St[M], Ed[M];
 50 bool Visit[N];
 51 int Ans;
 52
 53 inline void Input() {
 54     scanf("%d%d", &n, &m);
 55     For(i, 1, m)
 56         scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].c);
 57 }
 58
 59 inline int Find(int x) {
 60     int Len = 0;
 61     while(x != Fa[x]) {
 62         Stack[++Len] = x;
 63         x = Fa[x];
 64     }
 65     For(i, 1, Len) Fa[Stack[i]] = x;
 66     return x;
 67 }
 68
 69 inline int Work(int n) {
 70     int Ret = 1, T;
 71     For(i, 1, n) {
 72         For(j, i+1, n)
 73             while(C[j][i]) {
 74                 T = C[i][i]/C[j][i];
 75                 For(k, i, n) {
 76                     C[i][k] -= C[j][k]*T;
 77                     swap(C[i][k], C[j][k]);
 78                 }
 79                 Ret = -Ret;
 80             }
 81         if(!C[i][i]) return 0;
 82         Ret = Ret*C[i][i];
 83     }
 84     return abs(Ret);
 85 }
 86
 87 inline void Solve() {
 88     sort(E+1, E+1+m);
 89     For(i, 1, n) Fa[i] = i;
 90     Ans = 1, S = 0;
 91
 92     int Last = -1;
 93     For(i, 1, m)
 94         if(Last != E[i].c) {
 95             Ed[S] = i-1;
 96             S++, Last = E[i].c;
 97             St[S] = i;
 98         }
 99     Ed[S] = m;
100
101     int u, v, Cnt, Now;
102     For(k, 1, S) {
103         For(i, 1, n)
104             For(j, 1, n) Edge[i][j] = 0;
105         For(i, St[k], Ed[k]) {
106             u = Find(E[i].u), v = Find(E[i].v);
107             if(u == v) continue;
108             Edge[u][v]++, Edge[v][u]++;
109         }
110
111         For(i, 1, n) Visit[i] = 0;
112         For(i, St[k], Ed[k]) {
113             u = Find(E[i].u), v = Find(E[i].v);
114             if(u == v) continue;
115             Fa[u] = v, Visit[u] = Visit[v] = 1;
116         }
117
118         For(i, 1, n) Len[i] = 0;
119         For(i, 1, n)
120             if(Visit[i]) {
121                 u = Find(i);
122                 Con[u][++Len[u]] = i;
123             }
124
125         Now = 1;
126         For(i, 1, n)
127             if(Len[i]) {
128                 Cnt = Len[i];
129
130                 For(x, 1, Cnt)
131                     For(y, 1, Cnt) C[x][y] = 0;
132                 For(x, 1, Cnt)
133                     For(y, x+1, Cnt)
134                         C[x][y] = C[y][x] = -Edge[Con[i][x]][Con[i][y]];
135                 For(x, 1, Cnt)
136                     For(j, 1, Cnt) C[x][x] += Edge[Con[i][x]][Con[i][j]];
137
138                 Now = Now*Work(Cnt-1);
139             }
140
141         Ans = (Ans*Now)%Mod;
142     }
143
144     For(i, 1, n-1)
145         if(Find(i) != Find(i+1)) {
146             printf("0\n");
147             return;
148         }
149     printf("%d\n", Ans);
150 }
151
152 int main() {
153     SetIO("1016");
154     Input();
155     Solve();
156     return 0;
157 }

时间: 2024-11-03 22:14:01

bzoj1016的相关文章

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

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

Bzoj1016 最小生成树计数

Time Limit: 1000MS   Memory Limit: 165888KB   64bit IO Format: %lld & %llu Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了. Input 第一行包含两个数,n和m,其中1<=n<=1

[BZOJ1016][JSOI2008]最小生成树计数(结论题)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1016 分析: 首先有个性质:如果边集E.E'都可以表示一个图G的最小生成树(当然E和E’的元素个数肯定一样),那么某确定权值的边在E中出现的次数==在E‘中出现的次数 简单证明一下: 按照Kruskal算法的流程来想,首先我们知道Kruskal求一个最小生成树是正确的,那么不同的最小生成树会怎么产生呢?当然是Kruskal选择权值相同的边的顺序,很有可能选择权值相同边的顺序不同导致后

bzoj1016 [JSOI2008]最小生成树计数

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

BZOJ1016 JSOI2008 最小生成树计数 生成树+DFS

题意:求最小生成树的方案数,保证每个边权出现的次数小于十次. 题解:首先我们需要知道:一张图对于一个确定的边权,在任意最小生成树中出现的次数是相同的(请不要问我为什么QAQ).所以我们先求出每一种边权在MST中出现的次数,然后枚举每一个边权,暴力看取哪些边可以组出一颗MST,复杂度O(M*2^10*M/10) #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream>

[BZOJ1016] [JSOI2008] 最小生成树计数 (Kruskal)

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

bzoj1016 SCOI2008 最大数Maxnumber

题意: 有一个序列,首先有两种操作:①查找后L个数中最大数:②[(最近一次查询得到的答案 + 常数R) % 常数D] 得到一个新数插入到序列的末尾,如果序列中没有数,那么用 0 代替 最近一次查询得到的答案. 题解:一个数插入到序列中,查询时她会影响到前面比它小的数:也就是说一个新数插入,那么这个数前面的比它小的数将没有贡献.所以想到单调队列作为此题的核心算法. CODE: /* Author: JDD PROG: bzoj1012 最大数 DATE: 2015.9.22 */ #include

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

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

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

思路:模拟kruskal的过程,可以发现对于所有权值相同的边,有很多种选择的方案,而且权值不同的边并不会相互影响,因为先考虑权值较小的边,权值比当前权值大的边显然不在考虑范围之内,而权值比当前权值小的边所组成的连通块已经经过缩点变成一个点了,因此处理权值相同的所有边可以看成是一个阶段,最后的答案也就是所有阶段的答案的乘积(乘法原理). 那么如何来处理权值相同的方案数呢,同样还是考虑kruskal的过程,因为权值相同的边可能会组成很多个连通块,且连通块之间互不影响,因此只考虑单个连通块即可(还是乘