TZOJ6558: 城镇封锁(Tarjan、割点、子树贡献、乘法原理、找规律)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值