原题链接Critical Structures
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define mp make_pair
#define newline puts("")
const int maxn = 1010;
int n,m;
vector<int> G[maxn];
int dfn[maxn],low[maxn],cnt = 0;
stack<int> st;
int ct = 0;//联通分量的数量
// int book[maxn],num[maxn];//每个点所在环的编号以及每个环上点的数量
vector<int> cir[maxn];//cir[k]表示第k个环上的点是哪些
bool is_cut[maxn];//是否是割点
int fa[maxn];
map<pii,bool> is_bridge;
int par[maxn],siz[maxn];
int ans1,ans2,ans3,ans4;
int getpa(int x){
return x == par[x] ? x : par[x] = getpa(par[x]);
}
int gcd(int a,int b){
return !b ? a : gcd(b,a%b);
}
void tarjan(int u,int f)//第二个参数主要是传父亲
{
dfn[u] = low[u] = ++cnt;
st.push(u);
fa[u] = f;
int rt_num = 0;
for (auto v : G[u])
{
if(!dfn[v]){
tarjan(v,u);
rt_num ++;
low[u] = min(low[u],low[v]);
if((rt_num >= 2 && fa[u] == 0) || (fa[u]!=0 && low[v] >= dfn[u])) is_cut[u] = true;
if(low[v] > dfn[u]) {//u,v这条边是桥
is_bridge[mp(min(u,v),max(u,v))] = true;
ans2++;
}
}
else if(v != fa[u])
{
low[u] = min(low[u],dfn[v]);
}
}
if(dfn[u] == low[u])
{
++ct;
while(1)
{
int x = st.top();
st.pop();
cir[ct].push_back(x);
if(x == u) break;
}
}
}
void Clear()
{
ans1 = ans2 = ans3 = 0;
ans4 = 1;
for (int i=1;i<=ct;i++) cir[i].clear();
cnt = ct = 0;
for (int i=1;i<=n;i++){
G[i].clear();
fa[i] = 0;
is_cut[i] = false;
par[i] = 0;
siz[i] = 0;
dfn[i] = low[i] = 0;
}
while(!st.empty()) st.pop();
is_bridge.clear();
}
void solve()
{
scanf("%d%d", &n, &m);
for (int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d", &u,&v);
G[u].push_back(v),G[v].push_back(u);
}
tarjan(1,0);
// for (int i=1;i<=n;i++)
// {
// cout<<"i = "<<i<<" dfn = "<<dfn[i]<<" low = "<<low[i]<<endl;
// }
// for (int i=1;i<=n;i++) cout<<"i = "<<i<<" is_cut = "<<is_cut[i]<<endl;
for (int i=1;i<=n;i++) ans1 += (int)is_cut[i];//割点数量要在外面加
ans3 += ans2;
for (int i=1;i<=n;i++) par[i] = i,siz[i] = 0;
for (int u=1;u<=n;u++)
{
for (auto v : G[u])
{
if(v < u) continue;
if(is_bridge[mp(u,v)]) continue;
int f1 = getpa(u),f2 = getpa(v);
if(f1 > f2) swap(f1,f2);
if(f1 != f2)
{
par[f2] = f1;
siz[f1] += siz[f2] + 1;
}
else siz[f1]++;
}
}
for (int i=1;i<=n;i++)
{
int fi = getpa(i);
if(i == fi && siz[fi] > 0) ans3++;
ans4 = max(ans4,siz[fi]);
}
// cout<<"ans3 = "<<ans3<<" ans4 = "<<ans4<<endl;
int g = gcd(ans3,ans4);
printf("%d %d %d %d
",ans1,ans2,ans3/g,ans4/g);
Clear();
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
int T;
scanf("%d",&T);
for(int _=1;_<=T;_++)
{
solve();
}
return 0;
}