3624: [Apio2008]免费道路

3624: [Apio2008]免费道路

https://www.lydsy.com/JudgeOnline/problem.php?id=3624

题意:

  一张无向图,每种边有两种类型0和1。求一个最小生成树使得有k条0边。

分析:

  为了满足有k条0边的限制,先考虑0边哪些必选,如果所有1边都加入后,还有0边可以使得图不连通,那么这些0边必须选。

  把必须选的加上然后再加到k,然后再加1边。中间判一下是否必选的大于k,0边是否大于等于k。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4
 5 inline int read() {
 6     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch==‘-‘)f=-1;
 7     for(;isdigit(ch);ch=getchar())x=x*10+ch-‘0‘;return x*f;
 8 }
 9
10 const int N = 20010;
11 const int M = 100010;
12
13 struct Edge{
14     int u,v;
15     Edge() {}
16     Edge(int a,int b) {u = a, v = b;}
17 }e[M];
18 int fa[N], q[M];
19 bool vis[M];
20
21 int find(int x) {
22     return x == fa[x] ? x : fa[x] = find(fa[x]);
23 }
24
25 int main () {
26
27     int n = read(), m = read(), k = read();
28
29     int B = 0, W = m + 1;
30     for (int i=1; i<=m; ++i) {
31         int u = read(), v = read(), c = read();
32         if (c == 1) e[++B] = Edge(u,v);
33         else e[--W] = Edge(u,v);
34     }
35
36     for (int i=1; i<=n; ++i) fa[i] = i;
37
38     int tot = 0;
39     for (int i=1; i<=m; ++i) {
40         int u = find(e[i].u), v = find(e[i].v);
41         if (u == v) continue;
42         fa[u] = v;
43         if (i > B) vis[i] = true, q[++tot] = i;
44     }
45
46     if (tot > k) {
47         puts("no solution");
48         return 0;
49     }
50
51     for (int i=1; i<=n; ++i) fa[i] = i;
52
53     for (int i=1; i<=tot; ++i)
54         fa[find(e[q[i]].u)] = find(e[q[i]].v);
55
56     for (int i=W; i<=m; ++i) {
57         if (tot == k) break;
58         int u = find(e[i].u), v = find(e[i].v);
59         if (u == v) continue;
60         fa[u] = v;
61         tot ++;
62         vis[i] = true;
63     }
64
65     if (tot < k) {
66         puts("no solution");
67         return 0;
68     }
69
70     for (int i=1; i<=B; ++i) {
71         int u = find(e[i].u), v = find(e[i].v);
72         if (u == v) continue;
73         fa[u] = v;
74         vis[i] = true;
75     }
76
77     for (int i=1; i<=m; ++i)
78         if (vis[i]) printf("%d %d %d\n", e[i].u, e[i].v, i<=B?1:0);
79
80     return 0;
81 }

原文地址:https://www.cnblogs.com/mjtcn/p/9357005.html

时间: 2024-10-13 21:47:06

3624: [Apio2008]免费道路的相关文章

bzoj 3624: [Apio2008]免费道路 生成树的构造

3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 111  Solved: 49[Submit][Status] Description Input Output Sample Input 5 7 2 1 3 0 4 5 1 3 2 0 5 3 1 4 3 0 1 2 1 4 2 1 Sample Output 3 2 0 4 3 0 5 3 1 1 2 1 还是看的网上的标

BZOJ 3624: [Apio2008]免费道路 [生成树 并查集]

题意: 一张图0,1两种边,构造一个恰有k条0边的生成树 优先选择1边构造生成树,看看0边是否小于k 然后保留这些0边,补齐k条,再加1边一定能构成生成树 类似kruskal的证明 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=2e4+5, M=1e5+5; typedef long

[Apio2008]免费道路[Kruscal]

3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1292  Solved: 518[Submit][Status][Discuss] Description Input Output Sample Input 5 7 2 1 3 0 4 5 1 3 2 0 5 3 1 4 3 0 1 2 1 4 2 1 Sample Output 3 2 0 4 3 0 5 3 1 1 2

[APIO2008]免费道路

# [APIO2008]免费道路 ### 第一反应 考虑朴素的克鲁斯卡尔算法加一个限制,先选鹅卵石路,且选到k个就停止 带来的问题: - ~~叶子节点特殊处理,都选上~~(但其实是连通性) - ~~而且你诡异的发现,tm,这个鹅卵石路可以突破最小生成树!!!~~(不仔细看题面的后果) ### 正解 考虑上文中的连通性,先用水泥路跑一遍$Kruskal$,然后不连通的且用到鹅卵石路的都要选上.剩下的既然水泥路可以,那么鹅卵石路也可以代替嘛,先选鹅卵石路,选到$k$个就停止 emm,那么什么时候是无

bzoj3624: [Apio2008]免费道路

具体题目:https://vjudge.net/problem/HYSBZ-3624 Description 一个王国有N个城市和M条无向道路,这M条道路中有一些是鹅卵石路一些是水泥路.现在国王要选择尽可能少的路免费,并且使每两个城市都有一条免费路径.国王打算保留刚好K条鹅卵石路.请问国王是否可以办到所有这些要求,如果能则输出路径的两个城市和路的种类,否则"no solution". HINTN <= 20000M <= 100000 Analysis如果能成功肯定是最小生

【bzoj3624】Apio2008—免费道路

http://www.lydsy.com/JudgeOnline/problem.php?id=3624 (题目链接) 题意 给出一张无向图,其中有0类边和1类边.问能否构成正好有K条0类边的生成树,并输出方案. Solution 先将所有1类边加入生成树,然后再加入0类边,那么现在加入的0类边就是必须加入的0类边,将它们打上标记.然后再将并查集初始化,继续加0类边直到数量达到K,最后加1类边. 代码 // bzoj3624 #include<algorithm> #include<io

【[APIO2008]免费道路】

\(kruskal\)好题 \(0\)边的数量在某些情况下是可以无限制的调控的,前提是所有必须存在的边都在生成树里了 所以应该分别求出有哪些边是必须在生成树里的,我们可以先从大到小排序,求出有哪些\(0\)边必须在生成树里,之后再从小到大排序,求出那些\(1\)边必须在生成树里 之后剩下的边就可以随便放了,调控\(0\)边的个数恰好为\(k\)即可 代码 #include<iostream> #include<algorithm> #include<cstring> #

APIO2008免费道路

题目大意 给定一张n个点m条边的图,图上有两种边,求保证有k条第一种边的情况下的最小生成树 传送门 题解 考虑最小生成树kruskal算法 先找到不含限制的最小生成树,然后就可以知道哪些第一种边是必选的 然后跑第二遍kruskal,先把第一种边加到k条,然后加入第二种边就好 代码 #include<bits/stdc++.h> #define inf 1000000000 #define ll long long using namespace std; int read() { int x=

BZOJ 3624 免费道路

第一反应:这不先0后1做并查集就行了吗? 然后WA了... 哦....啊?哦...233 如果按顺序做并查集,有些0的边可能很重要(只能由它作为0连起两个联通块),但并没有被选. 于是先按1做并查集,选出这些边,再按0,1做并查集. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxv 20050 #define maxe 100500 u