[BZOJ1016][JSOI2008]最小生成树计数(Kruskal+计数)
2018-01-05 20:29
531 查看
先把边按权值排序。假设现在考虑到的是所有权值为w的边,并且所有权值小于w的合法边已经在最小生成树内。这样考虑一个贪心,把所有的权值为w都加入最小生成树内,但这样会形成环。又由于要求边权和最小的生成树,所以要在所有边权为w的边中,删掉尽可能少的边,使得当前的生成树没有环。这样等同于在当前的生成树内,删掉这些边后每对点之间的连通性不变。而一个m个点的连通图,去掉尽可能少的边使图仍然连通,答案一定是让这个连通图只剩下m−1条边。
归纳一下,得出结论:在一张图的每一个最小生成树中,同一权值的边的出现次数和连通的点集相同。
因此,先将边按权值排序,然后做一遍Kruskal,求出每种权值边的出现次数。
然后按顺序考虑每种权值:由于同种权值的边数≤10,因此可以210暴力枚举每条边选或不选,只要没有环并且选出边的条数恰好等于预处理出的次数,这种选法就是合法的。这时候,这种权值的结果就是合法方案的个数。
最后把每种权值的合法方案个数相乘,得到最终结果。
实现上的细节:
1、考虑完一种权值之后,要保留这种权值的边连通的集合。具体用个例子:n=4,m=5,有边(1,2,1),(1,3,1),(2,3,2),(2,4,2),(3,4,2)。(三元组的第三个元素表示边权)这时候如果不保留连通的集合,那么权值1的结果为1,权值2的结果为3,但最后结果是2而不是3,原因很简单:方案(1,2)(1,3)(2,3),虽然权值为1和2的边都构不成环,但是合起来就构成环了。但如果保留连通集合,也就是考虑完权值1之后,将(1,2)(1,3)(2,3)标记为已连通,那么权值为2的边的选择方案种,(2,3)就不是一个合法的方案。具体可以用并查集来实现。
2、注意图不联通的情况,输出0。
代码:
归纳一下,得出结论:在一张图的每一个最小生成树中,同一权值的边的出现次数和连通的点集相同。
因此,先将边按权值排序,然后做一遍Kruskal,求出每种权值边的出现次数。
然后按顺序考虑每种权值:由于同种权值的边数≤10,因此可以210暴力枚举每条边选或不选,只要没有环并且选出边的条数恰好等于预处理出的次数,这种选法就是合法的。这时候,这种权值的结果就是合法方案的个数。
最后把每种权值的合法方案个数相乘,得到最终结果。
实现上的细节:
1、考虑完一种权值之后,要保留这种权值的边连通的集合。具体用个例子:n=4,m=5,有边(1,2,1),(1,3,1),(2,3,2),(2,4,2),(3,4,2)。(三元组的第三个元素表示边权)这时候如果不保留连通的集合,那么权值1的结果为1,权值2的结果为3,但最后结果是2而不是3,原因很简单:方案(1,2)(1,3)(2,3),虽然权值为1和2的边都构不成环,但是合起来就构成环了。但如果保留连通集合,也就是考虑完权值1之后,将(1,2)(1,3)(2,3)标记为已连通,那么权值为2的边的选择方案种,(2,3)就不是一个合法的方案。具体可以用并查集来实现。
2、注意图不联通的情况,输出0。
代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; inline int read() { int res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + (c - 48); return bo ? ~res + 1 : res; } const int N = 105, M = 1005, ZZQ = 31011; int n, fa , m, d, cnt[M], pyz[M], sel[M], ans[M], L[M], R[M], F[M], lyt[M]; struct cyx { int u, v, w; } orz[M]; bool comp(cyx a, cyx b) { return a.w < b.w; } int cx(int x) { if (fa[x] != x) fa[x] = cx(fa[x]); return fa[x]; } bool zm(int x, int y) { int ix = cx(x), iy = cx(y); if (ix == iy) return 0; return fa[iy] = ix, 1; } void kruskal() { int i; for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= m; i++) if (zm(orz[i].u, orz[i].v)) sel[i] = 1; } void dfs(int cyx, int dep, int sta) { if (dep == R[cyx] + 1) { int i, cnt = 0; for (i = 1; i <= n; i++) fa[i] = F[i]; for (i = 0; i < R[cyx] - L[cyx] + 1; i++) if ((sta >> i) & 1) cnt++; if (cnt != pyz[cyx]) return; for (i = 0; i < R[cyx] - L[cyx] + 1; i++) if (((sta >> i) & 1) && !zm(orz[L[cyx] + i].u, orz[L[cyx] + i].v)) return; ans[cyx]++; if (ans[cyx] == 1) for (i = 1; i <= n; i++) lyt[i] = fa[i]; return; } dfs(cyx, dep + 1, sta); dfs(cyx, dep + 1, sta | (1 << dep - L[cyx])); } int main() { int i, j; n = read(); m = read(); for (i = 1; i <= m; i++) orz[i].u = read(), orz[i].v = read(), orz[i].w = read(); sort(orz + 1, orz + m + 1, comp); kruskal(); for (i = 1; i <= m;) { L[++d] = i; for (j = i; orz[i].w == orz[j].w && j <= m; j++) cnt[d]++, pyz[d] += sel[j]; R[d] = j - 1; i = j; } for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= d; i++) if (pyz[i] && cnt[i] == 1) zm(orz[L[i]].u, orz[L[i]].v); for (i = 1; i <= n; i++) F[i] = fa[i], fa[i] = i; for (i = 1; i <= d; i++) { if (!pyz[i]) continue; if (cnt[i] == 1) ans[i] = 1; else { dfs(i, L[i], 0); for (j = 1; j <= n; j++) F[j] = lyt[j]; } } int wohaocaia = 1; for (i = 1; i <= d; i++) if (pyz[i]) wohaocaia = wohaocaia * ans[i] % ZZQ; for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= m; i++) zm(orz[i].u, orz[i].v); for (i = 2; i <= n; i++) if (cx(1) != cx(i)) return printf("0\n"), 0; cout << wohaocaia << endl; return 0; }
相关文章推荐
- BZOJ 1016: [JSOI2008]最小生成树计数( kruskal + dfs )
- [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)
- 【BZOJ1016】【JSOI2008】最小生成树计数 & 【BZOJ1543】生成树计数 (kruskal+matrix_tree定理)
- 【Kruskal+dfs】BZOJ1016- [JSOI2008]最小生成树计数
- BZOJ.1016.[JSOI2008]最小生成树计数(Matrix Tree定理 Kruskal)
- BZOJ1016([JSOI2008]最小生成树计数)Kruskal+Matrix_Tree定理
- BZOJ 1016 [JSOI2008]最小生成树计数 Kruskal Matrix-Tree定理
- 【kruskal】【dfs】【JSOI 2008】【bzoj 1016】最小生成树计数
- BZOJ 1016 JSOI2008 最小生成树计数 Kruskal
- 【BZOJ】1016: [JSOI2008]最小生成树计数(kruskal+特殊的技巧)
- [BZOJ 1016][JSOI2008]最小生成树计数(Kruskal)
- BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)
- [BZOJ1016]JSOI2008最小生成树计数 |kruskal|乘法原理|dfs
- 【BZOJ1016】【JSOI2008】最小生成树计数 kruskal+dfs
- bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)
- BZOJ 1016: [JSOI2008]最小生成树计数 kruskal dfs
- [bzoj 1016] [JSOI2008]最小生成树计数:Kruskal,枚举
- BZOJ1016: [JSOI2008]最小生成树计数
- [BZOJ1016][JSOI2008]最小生成树计数(结论题)
- bzoj 1016: [JSOI2008]最小生成树计数