洛谷 P3623 [APIO2008] 免费道路
题目描述
新亚(New Asia)王国有 \(N\) 个村庄,由 \(M\) 条道路连接。其中一些道路是鹅卵石路,而其它道路是水泥路。保持道路免费运行需要一大笔费用,并且看上去王国不可能保持所有道路免费。为此亟待制定一个新的道路维护计划。
国王已决定保持尽可能少的道路免费,但是两个不同的村庄之间都应该有且仅有一条均由免费道路组成的路径连接。同时,虽然水泥路更适合现代交通的需要,但国王也认为走在鹅卵石路上是一件有趣的事情。所以,国王决定保持刚好 \(K\) 条鹅卵石路免费。
举例来说,假定新亚王国的村庄和道路如图 (a) 所示。如果国王希望保持两条鹅卵石路免费,那么可以如图 (b) 中那样保持道路 (1,2),(2,3),(3,4) 和 (3,5) 免费。该方案满足了国王的要求,因为:
- 两个村庄之间都有一条由免费道路组成的路径。
- 免费的道路已尽可能少。
- 方案中刚好有两条鹅卵石道路 (2,3) 和 (3,4)。

图 (a) 新亚王国中村庄和道路的一个示例。实线标注的是水泥路,虚线标注的是鹅卵石路。图(b) 一个保持两条鹅卵石路免费的维护方案。图中仅标出了免费道路。
给定一个关于新亚王国村庄和道路的数目以及国王决定保持免费的鹅卵石道路数目,写一个程序确定是否存在一个道路维护计划以满足国王的要求,如果存在则任意输出一个方案。
输入格式
输入第一行包含三个由空格隔开的整数:
\(N\),村庄的数目 \((1≤N≤2×10^4)\);
\(M\),道路的数目 \((1≤M≤10^5\));
\(K\),国王希望保持免费的鹅卵石道路数目 \((0≤K≤N−1)\)。
此后 \(M\) 行述了新亚王国的道路,编号分别为 1 到 \(M\)。第 \((i+1)\) 行述了第 \(i\) 条道路的情况。用 3 个由空格隔开的整数述:
$u_i $和 \(v_i\),为第 \(i\) 条道路连接的两个村庄的编号,村庄编号为 1 到 \(N\);
\(c_i\),表示第 \(i\) 条道路的类型。\(c_i=0\) 表示第 \(i\) 条道路是鹅卵石路,\(c_i=1\) 表示第 i 条道路是水泥路。
输入数据保证一对村庄之间至多有一条道路连接。
输出格式
如果满足国王要求的道路维护方案不存在,你的程序应该在输出第一行打印 no solution。 否则,你的程序应该输出一个符合要求的道路维护方案,也就是保持免费的道路列表。按照输入中给定的那样输出免费的道路。如果有多种合法方案,你可以任意输出一种。
输入输出样例
输入 #1复制运行
5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1
输出 #1复制运行
3 2 0
4 3 0
5 3 1
1 2 1
说明/提示
数据范围
对于 100% 的数据。保证 \(1≤N≤2×10^4\),\(1≤M≤10^5\),\(0≤K≤N−1\)。
算法分析
这题很模板,就是把所有水泥路都加进去,对所有石板路计算,看哪些是必须要加进去的,加入答案数组中,计算水泥路也是如此。但我们还要判无解,如果在算的过程中必须要的石板路大于\(K\),或必须要的水泥路大于\(N-1-K\),则无解。还有如果图不连通,也是无解,这一步在算石板路的时候就可以顺便算一下。到这里我们已经跑了两边生成树了。如果题目有解,就把剩下的,还不连通的点通过还未加进去的边连起来,把石板路凑成\(K\)条,水泥路凑成\(N-1-K\)条,最后再判断一下就可以了,一共3边生成树,不会TLE。
坑点
- 每跑完一边生成树,记得初始化fa数组。
- 要判无解。
- 无解要输出no solution,别拼错了。
- 此题输出的顺序随便,不用特殊处理。
- 最后构造的时候记得判无解。
把这些坑点都解决后,这题就可以完美AC了。
AC代码
献上我珍贵又丑陋又长的代码,禁止抄袭,否则会变棕(莫抄袭,棕了少年名,空悲切)。
因为作者太懒,代码又很长,就不打注释了。
#include<bits/stdc++.h>
using namespace std;
struct node{
int x,y,z;
};
int n,m,k;
int t = 0;
int tmp = 0;
int tot1,tot2;
int cnt = 0;
int cnt1 = 0;
int cnt2 = 0;
int fa[100005];
node a[100005];
node b[100005];
node c[100005];
int find(int x){
if(fa[x]==x){
return x;
}else{
return fa[x] = find(fa[x]);
}
}
void kruskal1(){
for(int i = 1; i<=cnt1; i++){
int x = find(a[i].x);
int y = find(a[i].y);
if(x!=y){
fa[x] = y;
c[++t] = a[i];
if(t>k){
return ;
}
}
}
tot1 = t;
}
void kruskal2(){
for(int i = 1; i<=cnt2; i++){
int x = find(b[i].x);
int y = find(b[i].y);
if(x!=y){
fa[x] = y;
c[++t] = b[i];
tot2++;
if(tot2>n-1-k){
break;
}
}
}
}
int main(){
cin>>n>>m>>k;
for(int i = 1; i<=n; i++){
fa[i] = i;
}
for(int i = 1; i<=m; i++){
int x,y,z;
cin>>x>>y>>z;
if(z==0){
a[++cnt1] = node{x,y,0};
}else{
b[++cnt2] = node{x,y,1};
}
}
for(int i = 1; i<=cnt2; i++){
int x = find(b[i].x);
int y = find(b[i].y);
if(x!=y){
fa[x] = y;
tmp++;
}
}
kruskal1();
if(t>k || t+tmp!=n-1){
cout<<"no solution";
return 0;
}
for(int i = 1; i<=n; i++){
fa[i] = i;
}
for(int i = 1; i<=cnt1; i++){
int x = find(a[i].x);
int y = find(a[i].y);
if(x!=y){
fa[x] = y;
}
}
kruskal2();
if(tot2>n-1-k){
cout<<"no solution";
return 0;
}
for(int i = 1; i<=n; i++){
fa[i] = i;
}
for(int i = 1; i<=t; i++){
int x = find(c[i].x);
int y = find(c[i].y);
if(x!=y){
fa[x] = y;
}
}
for(int i = 1; i<=cnt1; i++){
int x = find(a[i].x);
int y = find(a[i].y);
if(tot1<k && x!=y){
c[++t] = a[i];
fa[x] = y;
tot1++;
}
}
if(tot1!=k){
cout<<"no solution";
return 0;
}
for(int i = 1; i<=cnt2; i++){
int x = find(b[i].x);
int y = find(b[i].y);
if(tot2<n-1-k && x!=y){
c[++t] = b[i];
fa[x] = y;
tot2++;
}
}
if(t!=n-1){
cout<<"no solution";
return 0;
}
for(int i = 1; i<=t; i++){
cout<<c[i].x<<" "<<c[i].y<<" "<<c[i].z<<'\n';
}
return 0;
}
杜绝白嫖,点个赞加个关注再走吧。

浙公网安备 33010602011771号