【BZOJ 1016】[JSOI2008]最小生成树计数(搜索+克鲁斯卡尔)
来源:互联网 发布:什么优化软件好用 编辑:程序博客网 时间:2024/05/12 06:29
【题目链接】:http://www.lydsy.com/JudgeOnline/problem.php?id=1016
【题意】
【题解】
/* 两个最小生成树T和T'; 它们各个边权的边的数目肯定是一样的; 且相同边权的边; 那些边所形成的联通性是一样的; 可以考虑T和T'的形成; 比如说一开始 T和T'都是空的; 然后把边按边权从小到大排序后 找到的第一种边权的边权为 v1 且bian[left..right]都是这种边权的边; 然后假设T是我们正常用卡鲁斯卡尔算法搞出来的最小生成树; 那么T在这个阶段肯定会在这right-left+1条边中选择x条边; 那么这个时候我们再想想T'要怎么选? 假设我们选y条边 这里我们分类讨论一下 假如y>x, 这种情况是不可能的; 因为如果y可以大于x; 那就说明T还可以再加一条边权最小的边v1联通更多的分量; 而我们在做克鲁斯卡尔的时候已经保证了不能添加更多的分量; 所以可以排除; 假如y<x 这种情况也是不可能的; 假设我们少选了一条边 那么这条边链接的两个分量必然是要用更大的边权来链接; 这就不符合最小生成树了; 所也可以排除; 综上可知 x==y; 这里所选的y条边的各个节点的连通性必然和x条边的联通性一样; 因为如果不一样的话, 那么哪里不一样呢?? ->假设在T'中相对于T有两个分量没有连通在一起,那么你可以选择T中连接这两个分量的 边啊。而且因为是升序排的,所以肯定连了更优啊; 但是显然不可能再有这样的连法了,因为T是正确的最小生成树; 你如果能找到这样的连法的话,就说明T是不正确的了; 这就矛盾了; 根据上面的推导可知; 每个最小生成树; 出现的不同边权的种类是一样的,且各种边权的数目是确定的; 而且只要边权的数目确定了,这个边权造成的连通性的变化都是一样的; 所以我们只要枚举对于每一种边权,可以用哪些连着不同节点的边,但边权和它相同的边交换一下 一开始做的最小生成树的边就好了; 比如第i种边权的边v[i],出现的次数为numv[i]; 然后在用克鲁斯卡尔算法算出的最小生成树中v[i]出现的次数为k; 则从这numv[i]条边中选出k条边来,看看是不是这k条边中每条边都能对联通量贡献;(如果有一条边在做 的过程中边的两段链接的联通量已经是在一起的了,就退出,表示不行); 因为要回溯,所以这里的并查集不能加状态压缩。 当然n也不大就是了; 因为有说同边权的边的数目不超过10,所以2^10是可以接受的. 搜一波; 然后用乘法原理乘起来就好;*/
【完整代码】
#include <bits/stdc++.h>using namespace std;#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define LL long long#define rep1(i,a,b) for (int i = a;i <= b;i++)#define rep2(i,a,b) for (int i = a;i >= b;i--)#define mp make_pair#define pb push_back#define fi first#define se second#define rei(x) scanf("%d",&x)#define rel(x) scanf("%lld",&x)typedef pair<int, int> pii;typedef pair<LL, LL> pll;const int dx[9] = { 0,1,-1,0,0,-1,-1,1,1 };const int dy[9] = { 0,0,0,-1,1,-1,1,-1,1 };const double pi = acos(-1.0);const int M = 1100;const int N = 110;const int MOD = 31011;struct abc{ int x, y,z;};int n, m,f[N],tot = 0,tot1 = 0,num[M],sum = 0,ans = 1;abc bian[M];bool cmp1(abc a, abc b){ return a.z < b.z;}int ff(int x){ if (f[x] == x) return x; else return ff(f[x]);}void dfs(int l, int r, int now){ if (now == num[tot]) { sum++; return; } if (l > r) return; int x = bian[l].x, y = bian[l].y; int r1 = ff(x), r2 = ff(y); //这里f[r1]==r1,f[r2]==r2; if (r1 != r2) { f[r1] = r2; dfs(l + 1, r, now + 1); f[r1] = r1, f[r2] = r2; } dfs(l + 1, r, now);}int main(){ //freopen("F:\\rush.txt", "r", stdin); rei(n), rei(m); rep1(i, 1, m) rei(bian[i].x), rei(bian[i].y), rei(bian[i].z); sort(bian + 1, bian + 1 + m,cmp1); rep1(i, 1, n) f[i] = i; rep1(i, 1, m) { int l = i, r = i; while (r + 1 <= m && bian[r + 1].z == bian[l].z) r++; tot1++; rep1(j, l, r) { int x = bian[j].x, y = bian[j].y; int r1 = ff(x), r2 = ff(y); if (r1 != r2) { f[r1] = r2; tot++; num[tot1]++; } } i = r; } if (tot != n - 1) return puts("0"), 0; rep1(i, 1, n) f[i] = i; tot = 0; rep1(i, 1, m) { int l = i, r = i; while (r + 1 <= m && bian[r + 1].z == bian[l].z) r++; tot++; if (num[tot] == 0) { i = r;//。。。。。。bug点 continue; } sum = 0; dfs(l, r, 0); ans = (ans*sum) % MOD; rep1(j, l, r) { int r1 = ff(bian[j].x), r2 = ff(bian[j].y); if (r1 != r2) f[r1] = r2; } i = r; } printf("%d\n", ans); return 0;}
0 0
- 【BZOJ 1016】[JSOI2008]最小生成树计数(搜索+克鲁斯卡尔)
- 【BZOJ 1016】 [JSOI2008]最小生成树计数
- BZOJ 1016: [JSOI2008]最小生成树计数
- BZOJ 1016 [JSOI2008]最小生成树计数
- BZOJ 1016 [JSOI2008] 最小生成树计数
- [BZOJ]1016: [JSOI2008]最小生成树计数
- bzoj 1016: [JSOI2008]最小生成树计数
- bzoj 1016: [JSOI2008]最小生成树计数
- bzoj 1016: [JSOI2008]最小生成树计数
- BZoj 1016: [JSOI2008]最小生成树计数【最小生成树】
- BZOJ 1016 JSOI2008 最小生成树计数 Kruskal
- 【bzoj 1016】[JSOI2008]最小生成树计数 脑残是病
- [bzoj 1016] [JSOI2008]最小生成树计数:Kruskal,枚举
- 【BZOJ 1016】[JSOI2008]最小生成树计数 基尔霍夫矩阵||暴力
- [BZOJ 1016][JSOI2008]最小生成树计数(Kruskal)
- BZOJ 1016: [JSOI2008]最小生成树计数 kruskal dfs
- 最小生成树 克鲁斯卡尔算法
- 最小生成树(克鲁斯卡尔方法)
- TabBarController和其他view无法建立Relationship segue的原因
- Java中的深复制和浅复制
- 【汕头市选2014】舞伴(perm)
- 集成ShareSDK
- 关于codeblocks的debugger过程中出现failed情况的解决方案
- 【BZOJ 1016】[JSOI2008]最小生成树计数(搜索+克鲁斯卡尔)
- J2SE之集合框架
- C#109课的主要内容
- 系统集成项目管理工程师教程看书笔记4
- 如何判断一个类型是否重载了输出操作符
- 博客一夜回到解放前
- 读鸟哥Linux起始章后感
- css设置网页边框的三种形式
- parseInt(),parseFloat()