HDU 4253 Two Famous Companies(最小生成树+二分)
2015-09-25 15:08
344 查看
题意:就是说有A、B两个公司要修路,有m条路,可能是属于A修的,也可能是属于B修的,现在要求所有路都联通的情况下的最小权值,并且A公司必须要修k条路。
思路:一开始看了半天没思路...百度了下发现是一道clj的论文题,
思路是用f(x)表示A公司每条路加上x后图中最小生成树中最多含有的A公司的边数。
用g(x)表示A公司每条路加上x后图中最小生成树中最少含有的A公司的边数。
二分x,如果k在某个f(x)和g(x)之间那么此时这种情况就是我们要求的解,这个可以反证,如果不是最优解那么最优解的每条A边加上x代回这个子问题,一定比求得最小生成树权值和小,这样就推出矛盾。
题意:就是说有A、B两个公司要修路,有m条路,可能是属于A修的,也可能是属于B修的,现在要求所有路都联通的情况下的最小权值,并且A公司必须要修k条路。
思路:一开始看了半天没思路...百度了下发现是一道clj的论文题,
思路是用f(x)表示A公司每条路加上x后图中最小生成树中最多含有的A公司的边数。
用g(x)表示A公司每条路加上x后图中最小生成树中最少含有的A公司的边数。
二分x,如果k在某个f(x)和g(x)之间那么此时这种情况就是我们要求的解,这个可以反证,如果不是最优解那么最优解的每条A边加上x代回这个子问题,一定比求得最小生成树权值和小,这样就推出矛盾。
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define LL long long #define pii pair<int, int> //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; const int MAXN = 100010; struct Edge { int u, v, w, id; Edge(int u=0, int v=0, int w=0, int id=0) : u(u), v(v), w(w), id(id) {} bool operator < (const Edge& e) const { return w < e.w; } } e0[MAXN], e1[MAXN]; int n, m, k, ans, cnt0, cnt1, pa[MAXN]; int Find(int x) { if(pa[x] == x) return x; return pa[x] = Find(pa[x]); } int kruscal(int x) { for(int i = 0; i < n; i++) pa[i] = i; int len0 = 0, len1 = 0, ret = 0; Edge tmp; for(int i = 1; i <= m; i++) { if(len0>=cnt0 || len1<cnt1&&e1[len1].w<e0[len0].w+x) tmp = e1[len1++]; else tmp = e0[len0++]; int p = Find(tmp.u), q = Find(tmp.v); if(p != q) { ans += tmp.w; pa[p] = q; if(!tmp.id) ret++, ans+=x; } } return ret; } int main() { //reopen("input.txt", "r", stdin); int kase = 0; while(scanf("%d%d%d", &n, &m, &k) == 3) { cnt0 = cnt1 = 0; for(int i = 0; i < m; i++) { int u, v, w, id; scanf("%d%d%d%d", &u, &v, &w, &id); if(!id) e0[cnt0++] = Edge(u, v, w, id); else e1[cnt1++] = Edge(u, v, w, id); } sort(e0, e0+cnt0); sort(e1, e1+cnt1); int le = -100, ri = 100; while(le <= ri) { int mid = le + (ri-le)/2; ans = 0; int t1 = kruscal(mid+1); ans = 0; int t2 = kruscal(mid); if(t1<=k && t2>=k) { ans -= mid*k; break; } else if(t1 > k) le = mid + 1; else ri = mid - 1; } printf("Case %d: %d\n", ++kase, ans); } return 0; }
相关文章推荐
- 网络扫描技术揭秘
- Java学习笔记【接口】
- Windows系统下使用wmic命令查看硬件信息
- 找呀志_java网络编程(5)TCP和udp差额
- 【转】C#调用Windows图片和传真查看器打开图片
- android:inputType参数类型说明
- 2015北京网络赛 D-The Celebration of Rabbits 动归+FWT
- Java 7 中 NIO.2 的使用——第二节 元数据文件的属性
- AlertController(iOS8)
- 折半查找 && 在标准输出上打印行号
- CodeForces 1B Spreadsheets(implementation math)——Codeforces Beta Round #1
- Xcode7 beta 网络请求报错:The resource could not be loaded because the App Transport Security policy requir
- android 学习之路
- /*在DataTable中更新、删除数据*/
- 解决java和C语言之间DES加解密不一致的问题。
- 为什么你需要做邮件营销
- [原] 利用 OVS 建立 VxLAN 虚拟网络实验
- C/C++ 运算符优先级
- Mysql用户&权限管理
- hdu 4403 A very hard Aoshu problem(dfs)