cf1000 E. We Need More Bosses

题意:

在无向图中找两个点 \(x,y\)\(x\to y\) 必须经过的边最多

思路:

无向图 双连通分量 缩点

然后变成一棵树,求树的直径

const signed N = 3 + 3e5, M = N*2;
int n, m;

int h[N], e[M], ne[M], tot;
void add(int x, int y) {
    e[tot] = y, ne[tot] = h[x], h[x] = tot++;
}

//找桥
int dfn[N], low[N], num; bool bridge[M];
void tarjan(int x, int from) {
    dfn[x] = low[x] = ++num;
    for(int i = h[x]; ~i; i = ne[i]) {
        int y = e[i];
        if(!dfn[y]) {
            tarjan(y, i), low[x] = min(low[x], low[y]);
            if(low[y] > dfn[x]) bridge[i] = bridge[i^1] = 1;
        }
        else if(i != (from ^ 1)) //注意括号
            low[x] = min(low[x], dfn[y]);
    }
}
//找连通块
int c[N], dcc;
void dfs(int x) {
    c[x] = dcc;
    for(int i = h[x]; ~i; i = ne[i]) {
        int y = e[i];
        if(c[y] || bridge[i]) continue;
        dfs(y);
    }
}
//缩点
int hc[N], ec[M], nec[M], tc;
void add_c(int x, int y) {
    ec[tc] = y, nec[tc] = hc[x], hc[x] = tc++;
}
//直径
int d[N], vis[N], ans;
void dp(int x) {
    vis[x] = 1;
    for(int i = hc[x]; ~i; i = nec[i]) {
        int y = ec[i];
        if(vis[y]) continue; dp(y);
        ans = max(ans, d[x] + d[y] + 1);
        d[x] = max(d[x], d[y] + 1);
    }
}

void sol() {
    memset(h, -1, sizeof h), memset(hc, -1, sizeof hc);

    cin >> n >> m;
    while(m--) {
        int x, y; cin >> x >> y;
        add(x, y), add(y, x);
    }

    //找桥
    for(int i = 1; i <= n; i++)
        if(!dfn[i]) tarjan(i, 0);
    //每个点属于的连通块编号
    for(int i = 1; i <= n; i++)
        if(!c[i]) ++dcc, dfs(i);
    //缩点
    tc = 1;
    for(int i = 0; i < tot; i++) {
        int x = e[i^1], y = e[i];
        if(c[x] == c[y]) continue;
        add_c(c[x], c[y]);
    }
    //求直径
    dp(1);

    cout << ans;
}
posted @ 2022-07-09 12:58  Bellala  阅读(24)  评论(0)    收藏  举报