题目传送门
首先发现初始图五有向环的话那么肯定是“YES”,否则是“NO”。然后找到一种满足要求地建树规则即可。这里采用拓扑排序建树,先dfs找出目前点的拓扑序编号,要求从编号小的连向编号大的,然后根据编号大小给无向边确定方向。这样一定满足要求,因为如果一开始没有有向环,那么沿着有向边走点的拓扑序编号一定不断变大,所以不可能产生环。
#include<cstdio>
#include<set>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 50;
int T, n, m, cnt;
int num[N];
bool used[N];
vector<vector<int> > d;
vector<int> Topu;
void dfs(int u){
used[u] = 1;
for(auto &x : d[u]) if(!used[x]) dfs(x);
Topu.emplace_back(u);
}
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
cnt = 0;
bool ok = 1;
Topu.clear();
vector<pair<int, int> > e;
d = vector<vector<int> >(n + 1);
for(int i = 1; i <= n; ++i) used[i] = 0;
for(int i = 1, t, x, y; i <= m; ++i){
scanf("%d%d%d", &t, &x, &y);
if(t == 1) d[x].emplace_back(y);
e.emplace_back(make_pair(x, y));
}
for(int i = 1; i <= n; ++i) if(!used[i]) dfs(i);
for(int i = n - 1; ~i; --i) num[Topu[i]] = ++cnt;
for(int i = 1; i <= n; ++i){
for(auto &x : d[i]) if(num[i] > num[x]) ok = 0;
}
if(!ok) puts("NO");
else{
puts("YES");
for(auto &tt : e){
int x = tt.first, y = tt.second;
if(num[x] > num[y]) printf("%d %d
", y, x);
else printf("%d %d
", x, y);
}
}
}
return 0;
}