Codeforces 732F Tourist Reform

先用边-双连通的方式缩点,然后就能发现包含城市最多的那个点就是答案。点内肯定有环,直接连就行了,剩下的割边都指向城市最多的那个点。


代码:

#include<bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;
const int MAXN= 4e5+5;
vector<PII> G[MAXN];
vector<PII> edges;
int dfs_clock, tmp;
PII ans;
int pre[MAXN], iscut[MAXN], vis[MAXN];

int dfs1(int u, int fa) {
    int lowu = pre[u] = ++dfs_clock;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i].first, id = G[u][i].second;
        if(v == fa) continue;
        if(!pre[v]) {
            int lowv = dfs1(v, u);
            lowu = min(lowv, lowu);
            if(lowv > pre[u]) {
                iscut[id] = 1;
            }
        }
        else if(pre[v] < pre[u]) lowu = min(lowu, pre[v]);
    }
    return lowu;
}

void dfs2(int u, int fa) {
    if(!pre[u]) tmp++;
    pre[u] = 1;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i].first, id = G[u][i].second;
        if(v==fa || iscut[id]) continue;
        if(!vis[id]) {
            vis[id] = 1;
            edges[id] = make_pair(u, v);
            dfs2(v, u);
        }
    }
}

void dfs3(int u) {
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i].first, id = G[u][i].second;
        if(!vis[id]) {
            vis[id] = 1;
            if(iscut[id]) {
                edges[id] = make_pair(v, u);
            }
            dfs3(v);
        }
    }
}

int main() {
    memset(pre, 0, sizeof(pre));
    memset(iscut, 0, sizeof(iscut));
    int n, m;
    cin >> n >> m;
    edges.resize(m+1);
    for(int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        G[u].push_back(make_pair(v, i));
        G[v].push_back(make_pair(u, i));
    }
    dfs_clock = 0;

    dfs1(1, -1);
    memset(pre, 0, sizeof(pre));
    memset(vis, 0, sizeof(vis));
    ans.first = 0;
    for(int i = 1; i <= n; i++) if(!pre[i]) {
        tmp = 0;
        dfs2(i, -1);
        if(tmp > ans.first) {
            ans.first = tmp;
            ans.second = i;
        }
    }

    memset(vis, 0, sizeof(vis));
    dfs3(ans.second);

    printf("%d\n", ans.first);
    for(int i = 1; i <= m; i++) printf("%d %d\n", edges[i].first, edges[i].second);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值