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=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,K,top;
int fa[20005];
int u[100005],v[100005],c[100005];
int au[20005],av[20005],ac[20005];
bool mark[100005];
int num[2];
int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void solve(bool typ,int mx)
{
    for(int i=1;i<=m;i++)
        if(c[i]==typ&&num[typ]<mx)
        {
            int p=find(u[i]),q=find(v[i]);
            if(p!=q)
            {
                fa[p]=q;
                au[++top]=u[i],av[top]=v[i],ac[top]=c[i];
                mark[i]=1;num[typ]++;
            }
        }
}
int main()
{
    n=read();m=read();K=read();
    for(int i=1;i<=m;i++)
        u[i]=read(),v[i]=read(),c[i]=read();
    for(int i=1;i<=n;i++)fa[i]=i;
    num[0]=num[1]=0;
    solve(1,inf);solve(0,inf);
    if(num[1]+num[0]!=n-1||num[0]>K)
    {
        puts("no solution");
        return 0;
    }
    top=0;num[0]=num[1]=0;
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++)
        if(c[i]==0&&mark[i])
        {
            int p=find(u[i]),q=find(v[i]);
            if(p!=q)
            {
                num[0]++;fa[p]=q;
                au[++top]=u[i];av[top]=v[i];ac[top]=c[i];
            }
        }
    solve(0,K);solve(1,inf);
    if(num[0]<K)
    {
        puts("no solution");
        return 0;
    }
    for(int i=1;i<=top;i++)
        printf("%d %d %d\n",au[i],av[i],ac[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/yzhx/p/11740993.html

时间: 2024-10-11 05:35:48

APIO2008免费道路的相关文章

[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

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 还是看的网上的标

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

[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

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]免费道路】

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

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