【bzoj1123】【[POI2008]BLO】tarjan判割点

这里写图片描述
(上不了p站我要死了,侵权度娘背锅)

Description
Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。
Input
输入n<=100000 m<=500000及m条边
Output
输出n个数,代表如果把第i个点去掉,将有多少对点不能互通。
Sample Input
5 5
1 2
2 3
1 3
3 4
4 5
Sample Output
8
8
16
14
8

很明显的判割点
写来练练手,熟悉模板
割点的子树和其他部分不连通,除去子树的剩余部分与子树不连通。乘法原理。
注意会爆int

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long 
using namespace std;

inline int read(){
    int k=1,x=0;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')k=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*k;
}

const int N=100000+5;
const int M=500000+5;

int n,m;
int head[N],end[M*2],nxt[M*2],hh=1;
int idc,dfn[N],low[N];
ll siz[N],ans[N];
bool vis[M*2];

void adde(int a,int b){
    hh++;
    end[hh]=b;
    nxt[hh]=head[a];
    head[a]=hh;
}
void tarjan(int u){
    idc++;
    dfn[u]=low[u]=idc;
    siz[u]=1,ans[u]=n-1;
    bool bj=0;
    ll cnt=0,sizz=1;
    for(int i=head[u];i;i=nxt[i]){
        if(vis[i]) continue;
        cnt++;
        vis[i]=vis[i^1]=1;
        int v=end[i];
        if(dfn[v]){
            low[u]=min(low[u],dfn[v]);
        }
        else{
            tarjan(v);
            siz[u]+=siz[v];
            low[u]=min(low[v],low[u]);
            if(low[v]>=dfn[u]){
                bj=1;
                ans[u]+=siz[v]*(n-siz[v]);
                sizz+=siz[v];
            }
        }
    }
    if(u==1&&cnt<=1) ans[u]=(n-1)*2;
    if(u!=1&&bj==0) ans[u]=(n-1)*2;
    if(u!=1&&bj||u==1&&cnt>1) ans[u]+=(n-sizz)*sizz;
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=m;i++){
        int a,b;
        a=read(),b=read();
        adde(a,b),adde(b,a);
    }
    tarjan(1);
    for(int i=1;i<=n;i++) printf("%lld
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/LinnBlanc/p/7763097.html