题目大意
给定一张 (n) 个点,(m) 条边的图 (G),其中一部分边是有向边,一部分边是无向边。需要给所有给无向边定向,使得组成的新图是一个 DAG。
多测,(Tleq 2 imes10^4),(sum n,sum mleq 2 imes 10^5)。
符号及约定
(operatorname{edge}(u,v)) 表示节点 (u) 和 (v) 之间的边。(u o v) 表示一条从 (u) 到 (v) 的有向边。
Solution
先忽视所有无向边。如果剩余的所有有向边不能组成 DAG,那么无论无向边怎么定向也没用。
接下来考虑有向边没有问题时如何构造可行解即可。
不妨对有向边组成的 DAG 进行 topo sort,得到第 (i) 个节点的 topo sort 序不妨记为 (operatorname{ord}(i))。
任取一条无向边 (operatorname{edge}(u,v)),不妨令 (operatorname{ord}(u)<operatorname{ord}(v))(反正是无向边,并且方向待定,没有区别)。那么直接令 (operatorname{edge}(u,v)=u o v) 即可。原因是因为 (forall u,vin G')(这里 (G') 是一个 DAG)满足 (operatorname{ord}(u)<operatorname{ord}(v)),则不存在 (v o u) 这条边。
我们不妨形象地将 topo sort 所得的点序排列成一行,从左到右分别为 (operatorname{ord}(u)=1,2,3,...,n) 的节点。那么已经存在的构成 DAG 的边必然通通指向右边。
接下来我们也只需要保证我们所连的边也朝右,那么必然不可能存在环。
代码
#include<bits/stdc++.h>
#define HohleFeuerwerke using namespace std
#pragma GCC optimize(3,"Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#define int long long
HohleFeuerwerke;
inline int read(){
int s=0,f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) s=s*10+c-'0';
return s*f;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
const int MAXN=1e6+5;
int T,n,m,cntEdge,head[MAXN],ind[MAXN];
struct edge{
int type;
int from,to,next;
}e[MAXN];
vector<int> topo;
int refl[MAXN];
inline void add(int t,int u,int v){
cntEdge++; e[cntEdge].type=t;
e[cntEdge].from=u,e[cntEdge].to=v;
e[cntEdge].next=head[u],head[u]=cntEdge;
}
deque<int> q;
inline bool bfs(){
for(int i=1;i<=n;i++){
if(ind[i]==0) q.push_back(i);
}
while(!q.empty()){
int u=q.front(); q.pop_front();
topo.push_back(u);
for(int i=head[u];i;i=e[i].next){
if(e[i].type==1){
int v=e[i].to;
if(--ind[v]==0) q.push_back(v);
}
}
}
if(topo.size()==n) return true;
return false;
}
signed main()
{
T=read();
while(T--){
n=read(),m=read(),cntEdge=0;
topo.clear(); q.clear();
for(int i=1;i<=n+5;i++) ind[i]=head[i]=refl[i]=0;
for(int i=1;i<=m;i++){
int type=read();
int u=read(),v=read();
if(type==1) add(1,u,v),ind[v]++;
else add(0,u,v);
}
if(bfs()){
puts("YES");
for(int i=1;i<=n;i++) refl[topo[i-1]]=i;
for(int i=1;i<=m;i++){
if(e[i].type==0){
if(refl[e[i].to]<refl[e[i].from])
write(e[i].to),putchar(' '),write(e[i].from),puts("");
else write(e[i].from),putchar(' '),write(e[i].to),puts("");
}
else write(e[i].from),putchar(' '),write(e[i].to),puts("");
}
}
else puts("NO");
}
}