[POI2008] BLO-Blockade 题解

Page Views Count

[POI2008] BLO-Blockade 题解

题目大意

[POI2008] BLO-Blockade

Solution

鲜花:

加深了对 tarjan 的理解:

  1. tarjan 本质上就是一个 dfs。
  2. $low_u$ 一定不大于 $dfn_u$。

正解:

分析断开的点是不是割点。

如果不是割点,那么剩余图仍然联通,贡献为 $2(n-1)$。

如果是割点,那么图会被分为 $k$ 个连通块,贡献为:

$$ 2(n-1)+\sum\limits_{i=1}^k\sum\limits_{j=1}^k siz_i\times siz_j $$

搞掉第二个 $\sum$:

$$ 2(n-1)+\sum\limits_{i=1}^k siz_i\times (n-siz_i-1) $$

在实现中,如果遇到儿子不存在返租边($low_v\geq dfn_u$),则可以考虑子树连通块的贡献。最后记得计算子树内外的点形成点对产生的贡献($(n-\sum\limits_{i\in son_u} siz_i -1)\times \sum\limits_{i\in son_u} siz_i$)和该点和所有点形成点对的贡献($n-1$)。

最后因为是有序对所以记得乘 2。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>
using namespace std;

#define int long long

const int N=5e5+5;

int n,m;
vector<int> G[N];
int dfn[N],low[N],tim,sz[N],ans[N];

void tarjan(int u)
{
    dfn[u]=low[u]=++tim;
    sz[u]=1;
    int siz=0;
    for(int v:G[u]){
        if(!dfn[v]){
            tarjan(v);
            sz[u]+=sz[v];
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                ans[u]+=sz[v]*siz;
                siz+=sz[v];
            }
        }else
            low[u]=min(low[u],dfn[v]);
    }
    ans[u]+=(n-siz-1)*siz+n-1;
}

signed main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1,u,v;i<=m;i++){
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    tarjan(1);
    for(int i=1;i<=n;i++)
        cout<<ans[i]*2<<"\n";
    return 0;
}
Licensed under CC BY-NC-SA 4.0