CF1385E Directing Edges 题解
题目大意
给定一张 \(n\) 个点,\(m\) 条边的图 \(G\),其中一部分边是有向边,一部分边是无向边。需要给所有给无向边定向,使得组成的新图是一个 DAG。
多测,\(T\leq 2\times10^4\),\(\sum n,\sum m\leq 2\times 10^5\)。
符号及约定
\(\operatorname{edge}(u,v)\) 表示节点 \(u\) 和 \(v\) 之间的边。\(u\to 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\to v\) 即可。原因是因为 \(\forall u,v\in G'\)(这里 \(G'\) 是一个 DAG)满足 \(\operatorname{ord}(u)<\operatorname{ord}(v)\),则不存在 \(v\to 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");
}
}