题意:
给你一个混合图,判断是否有环。
分析:
想了很久还是看了题解,我始终没想到用并查集去判断无向边部分的环。对于无向边来说,可以用并查集判断环,若某条未处理的无向边u,v,其u和v在同一个连通分量中,则说明该图已存在环。无向边处理完后没有环的话,把属于同一个连通分量的结点收缩为一个结点,然后处理有向边,进行拓扑排序判断是否有环。
官方题解:
首先对于所有的无向边,我们使用并查集将两边的点并起来
若一条边未合并之前,两端的点已经处于同一个集合了,那么说明必定存在可行的环(因为这两个点处于同一个并查集集合中,那么它们之间至少存在一条路径)
如果上一步没有判断出环,那么仅靠无向边是找不到环的
考虑到,处于同一个并查集集合中的点之间必定存在一条路径互达,因此将一个集合的点合并之后,原问题等价于在新生成的有向图中是否有环
我们知道,有向无环图必定存在拓扑序,因此只需使用拓扑排序判定即可
以下附上代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cctype>
#include <cmath>
#include <stack>
#include <queue>
#include <list>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1000005;
int n,m1,m2;
struct UnionSet
{
int p[maxn];
void init(){
for(int i = 0; i <= n; i++){
p[i] = -1;
}
}
int find(int x){
return p[x] >= 0 ? p[x] = find(p[x]) : x;
}
void uinon(int u, int v){
u = find(u);
v = find(v);
int t = p[u] + p[v];
if(p[u] > p[v]){
p[v] = u;
p[u] = t;
}
else{
p[u] = v;
p[v] = t;
}
}
};
struct node
{
int v;
int next;
node(){}
node(int v_, int next_) : v(v_), next(next_){}
};
struct Adlist
{
int vex[maxn];
node arc[maxn*2];
int cnt;
void clear(){
for(int i = 0; i <= n; i++){
vex[i] = -1;
}
cnt = 0;
}
void insert(int u, int v){
arc[cnt].v = v;
arc[cnt].next = vex[u];
vex[u] = cnt;
cnt++;
}
};
UnionSet us;
Adlist li;
int inDegree[maxn];
bool vis[maxn];
int cnt;
queue<int> q;
bool topSort()
{
int i,j;
for(i = 1; i <= n; i++){
if(vis[i] && inDegree[i] == 0)
q.push(i);
}
int u,v;
for(i = 0; i < cnt; i++){
if(q.empty()) return false;
u = q.front(); q.pop();
for(j = li.vex[u]; j != -1; j = li.arc[j].next){
v = li.arc[j].v;
if(--inDegree[v] == 0){
q.push(v);
}
}
}
return true;
}
int main()
{
int t;
int u,v;
int i;
bool haveCircle;
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m1,&m2);
//
haveCircle = false;
us.init();
li.clear();
fill(inDegree,inDegree+n+1,0);
fill(vis,vis+n+1,0);
cnt = 0;
for(i = 0; i < m1; i++){
scanf("%d%d",&u,&v);
if(!haveCircle){
if(us.find(u) != us.find(v)){
us.uinon(u,v);
}
else{
haveCircle = true;
}
}
}// 0 to m1
for(i = 0; i < m2; i++){
scanf("%d%d",&u,&v);
//收缩点
u = us.find(u);
v = us.find(v);
li.insert(u,v);
if(!vis[u]) cnt++;
if(!vis[v]) cnt++;
vis[u] = 1;
vis[v] = 1;
inDegree[v]++;
}// 0 to m2
if(!haveCircle && !topSort()) haveCircle = true;
puts(haveCircle ? "YES" : "NO");
}
return 0;
}