ACWING363. B城(tarjan求割点)

B城有 n 个城镇,m 条双向道路。

每条道路连结两个不同的城镇,没有重复的道路,所有城镇连通。

把城镇看作节点,把道路看作边,容易发现,整个城市构成了一个无向图。

输入格式
第一行包含两个整数 n 和 m。

接下来m行,每行包含两个整数 a 和 b,表示城镇 a 和 b 之间存在一条道路。

输出格式
输出共n行,每行输出一个整数。

第 i 行输出的整数表示把与节点 i 关联的所有边去掉以后(不去掉节点 i 本身),无向图有多少个有序点(x,y),满足 x 和 y 不连通。

数据范围
n≤100000,m≤500000
输入样例:
5 5
1 2
2 3
1 3
3 4
4 5
输出样例:
8
8
16
14
8

思路: 每个点删掉,会剩下3种东西:
(1)他自己(2)删掉某边剩下的连通分量(3)剩下的连通分量
那么就是这几部分组合起来的答案。大小分别是1,size1,size2,size3…sizek,(n - 1 - ∑size)。答案就是这一块的答案乘以其与n的差。这可以在tarjan的过程求出来。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int maxn = 1000000 + 7;
int head[maxn],to[maxn],nex[maxn],tot;
int dfn[maxn],low[maxn],dfs_clock;
int size[maxn],cut[maxn];
ll ans[maxn];
int n,m;

void add(int x,int y)
{
    to[++tot] = y;
    nex[tot] = head[x];
    head[x] = tot;
}

void tarjan(int x)
{
    low[x] = dfn[x] = ++dfs_clock;
    int flag = 0,sum = 0;
    size[x] = 1;
    for(int i = head[x];i;i = nex[i])
    {
        int y = to[i];
        if(!dfn[y])
        {
            tarjan(y);
            size[x] += size[y];
            low[x] = min(low[x],low[y]);
            if(dfn[x] <= low[y])
            {
                flag++;
                sum += size[y];
                ans[x] += (ll)size[y] * (n - size[y]);
                if(x != 1 || flag >= 2)
                    cut[x] = 1;
            }
            
        }
        else
            low[x] = min(low[x],dfn[y]);
    }
    if(cut[x])
        ans[x] += (ll)(n - sum - 1) * (sum + 1) + (n - 1);
    else
        ans[x] = 2 * (n - 1);
}

int main()
{
    scanf("%d%d",&n,&m);
    tot = 1;
    for(int i = 1;i <= m;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    
    tarjan(1);
    for(int i = 1;i <= n;i++)
        printf("%lld\n",ans[i]);
    return 0;
}

上一篇:图论 —— tarjan 缩点 割点 (学习历程)


下一篇:WebService 的创建,部署和使用