题面
<center>2026: [POI2008]CLO<center>
<center>时间限制:10秒 内存限制:162MB<center>
题目描述
Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 你要把其中一些road变成单向边使得:每个town都有且只有一个入度。
输入
第一行输入n m.1 <= n<= 100000,1 <= m <= 200000 下面M行用于描述M条边.
输出
TAK或者NIE 常做POI的同学,应该知道这两个单词的了...
样例输入
4 5
1 2
2 3
1 3
3 4
1 4
样例输出
TAK
思路
题解
首先说一下,我的做题过程。本来以为是一道简单的图论+贪心题,但是却一直wa,百思不得其解……,在网上搜了题解,才明白过来,自己有一个图论的知识点,理解错了:无向边不贡献出度,也不贡献入度,but me think 无向边既贡献出度,也贡献入度
这道题用并查集做,(建图的话,数据量太大,存储不下),首先,初始化:N个节点,每个建立一个集合,第i个节点所在集合编号为i;然后处理m条边,只要两个节点之间有边,就把他们放到一个集合里面,如果加的边的两个节点在同一个集合里,那么这个集合标记为true(初始所有集合为false);处理完所以边之后,判断:只要有一个集合为false,则ans=NIE;只有所有集合均为true,ans=TAK。
源码
/************并查集***************/
#include<iostream>
#include <cstring>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1e5+5;
int fa[N];//f[i]表示节点i与谁在一个集合,初始f[i]=i,
//如:f[1]=2,表示1与2在一个集合,但2有可能在其他集合,所以i不一定在集合2
bool can[N];//can[i]表示集合i是否可行,初始值均为false
int find(int x){//核心函数,查找节点x在哪个集合
if (fa[x]==x) return x;//在本身集合,直接返回
fa[x]=find(fa[x]);//在其他集合,看其他集合是否加入新集合,并更新
return fa[x];//返回所在集合
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n,m,i,x,y,a,b;
cin>>n>>m;
mem(can, false);//初始化can[],均为false
for (i=1;i<=n;i++) fa[i]=i;//初始化f[],f[i]=i
for (i=1;i<=m;i++){
cin>>a>>b;
x=find(a),y=find(b);//分别查找a,b所在集合
x!=y?(fa[x]=y,can[y]|=can[x]):can[fa[x]]= true;//若a,b在同一集合,则该集合改为true;
//否则,将a所在集合x加入b所在集合y,并修改集合y的can值,只要集合x、y有一个为true则该集合为true;
}
for (i=1;i<=n;i++){
if (!can[find(i)]){//只要有一个节点所在集合为false,那么就不行(NIE)
cout<<"NIE\n";return 0;
}
}
cout<<"TAK\n";//所有节点所在集合为true,才可以(TAK)
return 0;
}
/*
4 4
1 2
1 3
1 4
2 4
*/