题目
给你无向连通图,问你去掉一个点的所有关联边之后,满足 (x, y) 不连通有多少个有序点 ?
思路
tarjan()
求割点,如果当前点不是割点,去掉这个点之后是一个 n-1 连通图,但注意题目中不去掉这个点,那么当前点的答案就是 2 * (n - 1) ;
如果当前点是割点,假设割点切割出来 c 个连通块,思考如何 O(n) 算有序点对
首先是 O(n^2) 算,就是 c i c_i ci 去乘以所有非 i 连通块,手列一下式子很好化简,大概就是 ∑ i = 1 c C i ∗ ( n − C i ) \sum_{i=1}^cC_i*(n-C_i) ∑i=1cCi∗(n−Ci)
现在就是思考怎么求割出去的连通块大小 ?
在 tarjan()
过程中,如果 low[v]>=dfn[u],那么去掉 u 之后,v 会被分割出来一个 sz[v] 的连通块 ; 对于每一个 v 都是如此,去掉这些能被分割出来的连通块之后,剩下的图还是一个连通块
根据这个性质就好做了
#include<bits/stdc++.h>
using namespace std;
#define int long longint const N = 1e5 + 10;int n, m;
vector<int> e[N];
int dfn[N], low[N], tot = 0;
bool is_Cut[N];
int ans[N], sz[N]; // 以 i 为根子树的大小void tarjan(int u, bool isRoot){dfn[u] = low[u] = ++ tot;int sum = 0;sz[u] = 1;int childCount = 0;for(int v : e[u]){if(!dfn[v]){tarjan(v, false);sz[u] += sz[v];low[u] = min(low[u], low[v]);if(low[v] >= dfn[u]){childCount ++;sum += sz[v];ans[u] += sz[v] * (n - sz[v]);}}else{low[u] = min(low[u], dfn[v]);}}if(childCount > isRoot){is_Cut[u] = true;ans[u] += n - 1;ans[u] += (n - sum - 1) * (sum + 1);}else{ans[u] = 2 * (n - 1);}
}
void tarjan(){for(int i = 1; i <= n; i ++){if(!dfn[i]){tarjan(i, true);}}
}void solve(){cin >> n >> m;for(int i = 1; i <= m; i ++){int u, v;cin >> u >> v;e[u].push_back(v);e[v].push_back(u);}tarjan();for(int i = 1; i <= n; i ++){cout << ans[i] << '\n';}
}signed main(){ios::sync_with_stdio(false);cin.tie(0), cout.tie(0); solve();return 0;
}