BZOJ1123 || 洛谷P3469 [POI2008]BLO【Tarjan割点】

Time Limit: 10 Sec
Memory Limit: 162 MB

Description

Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。

Input

输入n<=100000 m<=500000及m条边

Output

输出n个数,代表如果把第i个点去掉,将有多少对点不能互通。


题目分析

结点 u u u不是割点,那么去掉他只有 u u u和其他 n − 1 n-1 n1个点不连通
结点 u u u是割点,则需要 将去掉 u u u后产生的连通块两两相乘累加进入答案

Tarjan求割点的过程其实是一个建立搜索树的过程,我们可以从这里入手

假设搜索树上结点 u u u的子节点集合为 { v i } \{v_i\} {vi},其中有 k k k个点满足其是割点
那么去掉 u u u后将产生至多 k + 2 k+2 k+2个连通块
1.结点 u u u单独在一个连通块
2.搜索树上 u u u k k k个满足割点判断的点与他们自己的子树各自构成一个连通块
3.若搜索树上 u u u的子节点数量大于k,那么剩下的点构成一个连通块

由此我们可以在Trajan的过程中顺便记录搜索树上每个子树的大小size[u]
那么去掉 u u u后产生的不连通的有序对数量为

∑ i = 1 k s i z e [ v i ] ∗ ( n − s i z e [ v i ] ) + 1 ∗ ( n − 1 ) + ( n − 1 − ∑ i = 1 k s i z e [ v i ] ) ∗ ( 1 + ∑ i = 1 k s i z e [ v i ] ) \sum_{i=1}^ksize[v_i]*(n-size[v_i])+1*(n-1)+(n-1-\sum_{i=1}^ksize[v_i])*(1+\sum_{i=1}^ksize[v_i]) i=1ksize[vi](nsize[vi])+1(n1)+(n1i=1ksize[vi])(1+i=1ksize[vi])


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

void print(lt x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+'0');
}

lt n,m;
lt tot,cnt;
struct node{lt v,nxt;}E[1000010];
lt head[100010];
lt ans[100010];
lt dfn[100010],low[100010];
lt size[100010];
bool cut[100010];
lt rt;

void add(lt u,lt v)
{
    E[++tot].nxt=head[u];
    E[tot].v=v;
    head[u]=tot;
}

void tarjan(lt u)
{
    dfn[u]=low[u]=++cnt; size[u]=1;
    lt flag=0,sum=0;
    for(lt i=head[u];i;i=E[i].nxt)
    {
        lt v=E[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            size[u]+=size[v];
            low[u]=min(low[u],low[v]);
            if(dfn[u]<=low[v])
            {
                flag++;
                ans[u]+=size[v]*(n-size[v]);
                sum+=size[v];
                if(u!=rt||flag>1) cut[u]=true;
            } 
        }
        else low[u]=min(low[u],dfn[v]);
    }
    if(cut[u]) ans[u]+=(n-sum-1)*(sum+1)+n-1;
    else ans[u]=2*(n-1);
}

int main()
{
    n=read();m=read();
    for(lt i=1;i<=m;i++)
    {
        lt u=read(),v=read();
        if(u==v) continue;
        add(u,v);add(v,u);
    }
    
    for(lt i=1;i<=n;i++)
    if(!dfn[i]) rt=i,tarjan(i);
    
    for(lt i=1;i<=n;i++)
    print(ans[i]),printf("\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值