The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest I题Critical Structures

原题链接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;
}
你将不再是道具,而是成为人如其名的人
原文地址:https://www.cnblogs.com/wsl-lld/p/15438284.html