TZOJ6558: 城镇封锁
描述
Byteotia 城市有 n 个城镇,m 条双向道路。每条道路连接两个不同的城镇,没有重复的道路,所有城镇连通。
输出 n 个数,代表如果把第 i 个点去掉,将有多少对点不能互通。
输入
输入 n,m 及 m 条边(n≤100000,m≤5×100000 )。
输出
输出 n 个数,代表如果把第 i 个点去掉,将有多少对点不能互通。
样例输入
5 5
1 2
2 3
1 3
3 4
4 5
样例输出
8
8
16
14
8
解题思路
画图分析后发现所求答案与割点和普通点存在联系,所以想到用Tarjan算法。样例如图:
当前图中根据Tarjan算法得到割点的有3和4,其余是普通结点。然后得到规律:删除普通点的答案是–(n - 1) * 2,删除割点时的答案是.(n - 1) * 2 加上 留存下来的连通分量的成员数相乘得到的积再乘2。比如删除3,留存的有12和45两个连通分量,答案是2 * 2 * 2 = 8。但是很明显这样O(N logN)的复杂度是通过不了的。所以,应该直接在Tarjan中去找割点和普通点的对应贡献值把它们的结果相加就是答案。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0), cin.tie(0)
const ll N = 1e5 * 1 + 5;
ll n, m, dfn[N], low[N], ti, st[N], sz[N], ans[N];
vector<ll> g[N];
void Tarjan(ll u, ll fa) {
dfn[u] = low[u] = ++ ti;
sz[u] = 1;
ll ct = 0;
ll sum = 0;
for(auto v : g[u]) {
if(!dfn[v]) {
++ ct;
Tarjan(v, u);
low[u] = min(low[u], low[v]);
sz[u] += sz[v];
if(u == fa && ct > 1 || u != fa && low[v] >= dfn[u]) {
st[u] = 1;
ans[u] += (sz[v] * sum) * 2;//割点u对应子树的贡献和
sum += sz[v];
}
} else if(v != fa) low[u] = min(low[u], dfn[v]);
}
if(st[u]) ans[u] += ((n - 1 - sum) * sum + (n - 1)) * 2;//除u和其子树外的其他子树的所有贡献 + 边数
else ans[u] += (n - 1) * 2;
}
void solve() {
cin >> n >> m;
for (int i = 0; i < m; ++i) {
ll u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; ++i) {
if(!dfn[i]) Tarjan(i, i);
}
for (int i = 1; i <= n; ++i) {
cout << ans[i] << endl;
}
}
int main( ){
IOS;
// ll t; cin >> t;
ll t = 1;
while (t--) solve();
return 0;
}