P3469 [POI 2008] BLO-Blockade

割点+计数的一个题(https://www.luogu.com.cn/problem/P3469)
如果想要重新建图在新图上计数的话大概需要一个圆方树,因为一个割点会属于多个点双,写起来也很麻烦。
所以可以在tarjan过程中直接找到子树内的连通块来计数就会比较简单。
题外话:
割点比较神奇的一件事就是每次是考虑它的父节点是不是割点而不是它本身。
感觉比边双稍微难理解一些。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
const int N = 6e5 + 7;
int read() {
    int res = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
    }
    return res;
}
int n, m, rt, dep[N];
vector<int>E[N], F[N];
int dfn[N], scc[N], tot, sc, low[N];
int siz[N];
int ans[N];
bool cut[N];
void tarjan(int x, int fa) {
    dfn[x] = low[x] = ++tot;
    siz[x] = 1;
    int sum = 0, f = 0;
    for (int i : E[x]) {
        if (!dfn[i]) {
            ++f;
            tarjan(i, x);
            siz[x] += siz[i];
            low[x] = min(low[x], low[i]);
            if (low[i] >= dfn[x]) {
                // 是子树并且已经找到连通分量的块才需要加进去,否则会合并到上面的块内
                ans[x] += siz[i] * (n - siz[i]);
                sum += siz[i];
                if (f > 1 || x != 1) cut[x] = 1;
            }
        }
        else if (1){
            // 这里的话理论上是不访问父节点的,但是因为对于割点来说与父节点相等也无所谓,所以不会影响结果
            low[x] = min(low[x], dfn[i]);
        }
    }
    if (!cut[x]) ans[x] = 2*(n -1);
    else ans[x] += (n - sum - 1) * (sum + 1) + n - 1;
} 
void dfs1(int x, int fa) {
    dep[x] = dep[fa] + 1;
    for (int i : F[x]) {
        if (i == fa) continue;
        dfs1(i, x);
    }
    if (dep[x] > dep[rt]) rt = x;
}
void solve() {
    n = read(), m = read();
    for (int i = 1; i <= m; i++) {
        int x = read(), y = read();
        E[x].push_back(y);
        E[y].push_back(x);
    }
    tarjan(1, 0);
    for (int i = 1; i <= n; i++) cout << ans[i] << endl;
}
signed main(){
    int t = 1;
    // cin >> t;
    while(t--){
        solve();
    }

}
posted @ 2025-05-15 19:43  lyrrr  阅读(8)  评论(0)    收藏  举报