题目链接

我们发现有些白边是必须加的,有些是多余的。

那么我们先把所有黑边加进去,然后把必须要加的白边找出来。

然后Kruskal,把必须要加的白边先加进去,小于K的话再加能加的白边。然后加黑边。

要求最后是一棵树,没注意,刚开始以为白边还要多判次。

简便的写法:

//2464kb	96ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=2e4+5,M=1e5+5; int fa[N];
bool chose[M];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
int fr,to,val;
inline void Print(){
printf("%d %d %d\n",fr,to,val);
}
bool operator <(const Edge &x)const{
return val<x.val;
}
}e[M]; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
#define Init() for(int i=1; i<=n; ++i) fa[i]=i
int Find(int x)
{
return x==fa[x]?x:fa[x]=Find(fa[x]);
}
bool Solve(int n,int m,int K)
{
Init();
std::sort(e+1,e+1+m);
int k=1;
for(int i=m,r1,r2; i; --i)
{
if((r1=Find(e[i].fr))==(r2=Find(e[i].to))) continue;
fa[r1]=r2;
if(!e[i].val) e[i].val=-1;//mark the white edge
if(++k==n) break;
}
if(k<n) return 0; Init();
std::sort(e+1,e+1+m);
int used=0; k=1;
for(int i=1,r1,r2; i<=m; ++i)
{
if((r1=Find(e[i].fr))==(r2=Find(e[i].to))) continue;
if(used<K || e[i].val==1)
{
fa[r1]=r2, chose[i]=1;
if(e[i].val<=0) e[i].val=0, ++used;
if(++k==n) break;
}
}
return used==K&&k==n;
} int main()
{
int n=read(),m=read(),K=read();
for(int i=1; i<=m; ++i) e[i]=(Edge){read(),read(),read()};
if(!Solve(n,m,K)) puts("no solution");
else for(int i=1; i<=m; ++i) if(chose[i]) e[i].Print(); return 0;
}

为了省掉sort闲的没事的写法:

//2904kb	76ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 250000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=2e4+5,M=1e5+5; int fa[N];
bool choseb[M],chosew[M];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Edge
{
int fr,to;
}b[M],w[M]; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
#define Init() for(int i=1; i<=n; ++i) fa[i]=i
int Find(int x)
{
return x==fa[x]?x:fa[x]=Find(fa[x]);
}
inline bool Union(int u,int v)
{
if(Find(u)!=Find(v)) {fa[fa[u]]=fa[v]; return 1;}
return 0;
}
bool Solve(int n,int m,int K,int bt,int wt)
{
if(wt<K) return 0;
Init();
int k=1;
for(int i=1; i<=bt; ++i) if(Union(b[i].fr,b[i].to)&&++k==n) break;
if(k<n)
for(int i=1; i<=wt; ++i)
{
if(!Union(w[i].fr,w[i].to)) continue;
chosew[i]=1;//mark the white edge
if(++k==n) break;
}
if(k<n) return 0; Init();
int used=0; k=1;
for(int i=1; i<=wt; ++i)
if(chosew[i]&&Union(w[i].fr,w[i].to))
if(++used, ++k==n) break;
if(used>K) return 0;
if(k<n&&used<K)
for(int i=1; i<=wt; ++i)
if(!chosew[i]&&Union(w[i].fr,w[i].to))
{
chosew[i]=1, ++used, ++k;
if(k==n||used==K) break;
}
if(used<K) return 0;
if(k<n)
for(int i=1; i<=bt; ++i)
{
if(!Union(b[i].fr,b[i].to)) continue;
choseb[i]=1;
if(++k==n) break;
}
return k==n;
} int main()
{
int n=read(),m=read(),K=read(),bt=0,wt=0;
for(int i=1,u,v; i<=m; ++i)
if(u=read(),v=read(),read()) b[++bt]=(Edge){u,v};
else w[++wt]=(Edge){u,v};
if(!Solve(n,m,K,bt,wt)) puts("no solution");
else
{
for(int i=1; i<=wt; ++i) if(chosew[i]) printf("%d %d 0\n",w[i].fr,w[i].to);
for(int i=1; i<=bt; ++i) if(choseb[i]) printf("%d %d 1\n",b[i].fr,b[i].to);
} return 0;
}

BZOJ.3624.[APIO2008]免费道路(Kruskal)的更多相关文章

  1. BZOJ 3624: [Apio2008]免费道路

    3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1201  Solved:  ...

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

    3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 111  Solved: 4 ...

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

    题意: 一张图0,1两种边,构造一个恰有k条0边的生成树 优先选择1边构造生成树,看看0边是否小于k 然后保留这些0边,补齐k条,再加1边一定能构成生成树 类似kruskal的证明 #include ...

  4. BZOJ 3624 [Apio2008]免费道路:并查集 + 生成树 + 贪心【恰有k条特殊路径】

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3624 题意: 给你一个无向图,n个点,m条边. 有两种边,种类分别用0和1表示. 让你求一 ...

  5. bzoj 3624: [Apio2008]免费道路【生成树+贪心】

    先把水泥路建生成树,然后加鹅卵石路,这里加的鹅卵石路是一定要用的(连接各个联通块),然后初始化并查集,先把必需的鹅卵石路加进去,然后随便加鹅卵石路直到k条,然后加水泥路即可. 注意判断无解 #incl ...

  6. Bzoj 3624: [Apio2008]免费道路 (贪心+生成树)

    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 这 ...

  7. 3624: [Apio2008]免费道路

    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 ...

  8. [Apio2008]免费道路[Kruscal]

    3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1292  Solved:  ...

  9. P3623 [APIO2008]免费道路

    3624: [Apio2008]免费道路 Time Limit: 2 Sec Memory Limit: 128 MBSec Special Judge Submit: 2143 Solved: 88 ...

随机推荐

  1. <meta content='IE=edge,chrome=1' http-equiv='X-UA-Compatible' />

    代码一:<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> http-eq ...

  2. Linux下/etc/passwd、/etc/shadow、/etc/group文件

    1./etc/passwd [root@prac ~]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbi ...

  3. bzoj千题计划258:bzoj3123: [Sdoi2013]森林

    http://www.lydsy.com/JudgeOnline/problem.php?id=3123 启发式合并主席树 #include<cmath> #include<cstd ...

  4. 给定一个整数,求解该整数最少能用多少个Fib数字相加得到

    一,问题描述 给定一个整数N,求解该整数最少能用多少个Fib数字相加得到 Fib数列,就是如: 1,1,2,3,5,8,13.... Fib数列,满足条件:Fib(n)=Fib(n-1)+Fib(n- ...

  5. flask_sqlalchemy的使用

    第一配置文件 # coding:utf-8 DIALECT = 'mysql' DRIVER = 'pymysql' USERNAME = 'root' PASSWORD = ' HOST = '12 ...

  6. google浏览器测试时清理缓存、强制不用缓存刷新快捷键(常用、效率)

    Ctrl+Shift+Del  清除Google浏览器缓存的快捷键  Ctrl+Shift+R  重新加载当前网页而不使用缓存内容

  7. Jenkins的安装及使用(一)

    操作环境:Windows7 一.环境准备 1 安装JDK 本文采用jdk-8u111-windows-x64.exe: 安装完成后配置环境变量. 2 配置tomcat 本文采用tomcat8,免安装版 ...

  8. JAVA不可变类与可变类、值传递与引用传递深入理解

    一个由try...catch...finally引出的思考,在前面已经初步了解过不可变与可变.值传递与引用传递,在这里再次深入理解. 1.先看下面一个try..catch..finally的例子: P ...

  9. Windows下设置oracle数据库定时备份

    1编写备份脚本 echo backup oracle database...... echo %~dp0 set file_dir=%~dp0 echo backup time...... set & ...

  10. python3之Splash

    Splash是一个javascript渲染服务.它是一个带有HTTP API的轻量级Web浏览器,使用Twisted和QT5在Python 3中实现.QT反应器用于使服务完全异步,允许通过QT主循环利 ...