题意:一个联通的无向图, 对于每一条边, 若删除该边后存在两点不可达,则输出这两个点, 如果存在多个则输出第一个点尽可能大,第二个点尽可能小的。 不存在输出0 0。
思路:很明显是求桥,写完了以后发现有更优美的写法貌似不用缩点,直接在Tarjan求桥边的过程中就可以计算出答案。
虽然写的恶心,但思路还是比较好懂,因为一条桥边将图分成两个连通分量,记录两个联通分量中最大的点max1 max2, 如果max1!=n 则答案就是max1 max1+1否则max2 max2+1,所以答案找出桥边缩点,然后以结点n为根进行一次dp,求出以每个点为根的子树中的最大值,并求出每个节点的深度,那么对于两个节点,答案就是深度大的那颗子树中的最大值和最大值加一。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int MAXN = 100100;//点数
const int MAXM = 200200;//边数,因为是无向图,所以这个值要*2
int n, m, T;
struct Edge
{
int from, to, next;
bool cut;//是否是桥标记
}edge[MAXM];
int head[MAXN], tot;
int Low[MAXN],DFN[MAXN],SLT[MAXN];
int Index;
bool vis2[MAXN];
int bridge;//桥的数目
vector<int> G[MAXN]; //所点后形成的图
int maxv[MAXN]; //每颗子树里的最大值
void addedge(int u,int v)
{
edge[tot].from = u;edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut=false;
head[u] = tot++;
}
int Tarjan(int u, int fa) { //u在dfs树中的父节点为fa
int lowu = DFN[u] = ++Index;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(!DFN[v]) { //没有访问过v
int lowv = Tarjan(v, u);
lowu = min(lowu, lowv); //用后代的low函数更新u的low函数
if(lowv > DFN[u]) {
edge[i].cut = 1;
edge[i^1].cut = 1;
}
}
else if(DFN[v] < DFN[u] && v != fa) lowu = min(lowu, DFN[v]); //用反向边更新u的low函数
}
Low[u] = lowu;
return lowu;
}
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
for(int i = 1; i <= n; i++) G[i].clear();
memset(maxv, -1, sizeof(maxv));
memset(vis2, 0, sizeof(vis2));
memset(DFN,0,sizeof(DFN));
}
int dep[MAXN];
void found_SLT(int cur, int id) {
SLT[cur] = id;
vis2[cur] = 1;
for(int i = head[cur]; i != -1; i = edge[i].next) {
int u = edge[i].to;
if(edge[i].cut || vis2[u]) continue;
found_SLT(u, id);
}
}
void dfs(int cur, int fa) {
for(int i = 0; i < G[cur].size(); i++) {
int u = G[cur][i];
if(u == fa) continue;
dep[u] = dep[cur] + 1;
dfs(u, cur);
maxv[cur] = max(maxv[cur], maxv[u]);
}
}
void solve()
{
bridge = Index = 0;
//
// if(m!=n-1) {
Tarjan(1,-1);
//cout << bridge << endl;
int id = 0;
for(int i = 1; i <= n; i++) {
if(!vis2[i]) found_SLT(i, ++id);
}
//cout << id << endl;
//for(int i = 1; i <= n; i++) SLT[i] = i;
for(int i = 1;i <= n;i++)
for(int j = head[i];j != -1;j = edge[j].next) {
int u = edge[j].to;
maxv[SLT[u]] = max(maxv[SLT[u]], u);
if(SLT[i]==SLT[u]) continue;
G[SLT[i]].push_back(SLT[u]);
}
//cout << G[1].size() << endl;
int root = SLT[n];
//cout << SLT[n] << endl;
dep[root] = 0;
//for(int i = 1; i <= n; i++) cout << maxv[i] << endl;
dfs(root, -1);
//for(int i = 1; i <= n; i++) cout << G[i].size() << endl;
//for(int i = 1; i <= n; i++) cout << dep[i] << endl;
//cout << " FUck " << endl;
}
int main() {
//freopen("1004.in", "r", stdin);
//freopen("input.txt", "r", stdin);
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
//if(T==1) cout << n << endl << m << endl;
init();
for(int i = 0; i < m; i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
solve();
//if(T==1) cout << "gan " << endl;
for(int i = 0; i < 2*m; i+=2) {
int u = edge[i].from, v = edge[i].to;
if(SLT[u]==SLT[v]) puts("0 0");
else {
if(dep[SLT[u]]>dep[SLT[v]]) printf("%d %d\n", maxv[SLT[u]], maxv[SLT[u]]+1);
else printf("%d %d\n", maxv[SLT[v]], maxv[SLT[v]]+1);
}
}
//cout << T << endl;
}
return 0;
}