tarjan的时候每次每次加入一条返祖边,都会至少缩去一个点,那么只要记录一下遍历过的边以及返祖边就可以了
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100010;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
char c=nc(); x=0;
for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}
int T,n,m,cnt,G[N],dfn[N],low[N],S[N],vis[N],tk[N],tcnt,top,t;
struct edge{
int s,t,nx;
}E[N];
inline void addedge(int x,int y){
E[++cnt].t=y; E[cnt].s=x; E[cnt].nx=G[x]; G[x]=cnt;
}
void dfs(int x){
dfn[x]=low[x]=++t; vis[x]=1;
S[++top]=x; int bck=0;
for(int i=G[x];i;i=E[i].nx){
if(!vis[E[i].t]){
dfs(E[i].t),low[x]=min(low[x],low[E[i].t]);
tk[i]=1,tcnt++;
}
if(vis[E[i].t]!=2){
if(low[E[i].t]<low[x])
low[x]=low[E[i].t],bck=i;
}
}
if(bck) tk[bck]=1,tcnt++;
if(low[x]==dfn[x]){
int u;
do{
u=S[top--]; vis[u]=2;
}while(u!=x && top);
}
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
read(T);
while(T--){
read(n); read(m);
for(int i=1,x,y;i<=m;i++)
read(x),read(y),addedge(x,y);
dfs(1);
for(int i=1;i<=m && tcnt<2*n;i++)
if(!tk[i]){
tk[i]=1; tcnt++;
}
for(int i=1;i<=m;i++) if(!tk[i]) printf("%d %d\n",E[i].s,E[i].t);
cnt=t=top=tcnt=0;
for(int i=1;i<=n;i++) G[i]=dfn[i]=low[i]=vis[i]=0;
for(int i=1;i<=m;i++) tk[i]=0;
}
}