[POI2008]BLO-Blockade 割点

[POI2008]BLO-Blockade 割点

题面

容易想到用( ext{Tarjan})求割点。对于非割点,会损失(2 imes(n-1))次访问(注意是互相访问,所以要乘2);对于割点,损失的访问次数即为(sum^{k}_{i=1}sz[i] imes(n-sz[i]))(割点被删后,产生(k)个联通块,第(i)个联通块大小为(sz[i]))。在求割点时顺便安装上述分类讨论即可。

注意:求完(u)的子树中所有割点后,还要加上剩下来的大联通块((n-sum-1) imes(sum+1))和自己(1 imes(n-1))次访问。

#include <cstdio>
#define MAXN 100010
#define MAXM 500010*2
#define MIN(A,B) ((A)<(B)?(A):(B))
#define MAX(A,B) ((A)>(B)?(A):(B))
#define ll long long
using namespace std;
int head[MAXN],vv[MAXM],nxt[MAXM],tot;
int read(){
    int w=1,q=0;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')q=q*10+ch-'0',ch=getchar();
    return w*q;
}
void add_edge(int u, int v){
    vv[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
int n,m;
ll ans[MAXN];
int dfn[MAXN],cnt,low[MAXN],sz[MAXN];
void tarjan(int u){
    dfn[u]=++cnt;
    low[u]=dfn[u];
    sz[u]=1;
    int son_sz=0,sum=0;
    bool cut=0;
    for(int i=head[u];i;i=nxt[i]){
        int v=vv[i];
        ++son_sz;
        if(dfn[v]==0){
            tarjan(v);
            low[u]=MIN(low[u], low[v]);
            sz[u]+=sz[v];
            if(low[v]>=dfn[u]){
                sum+=sz[v];
                ans[u]+=(ll)sz[v]*(n-sz[v]);
                if(u!=1) cut=1;
            }
        }else low[u]=MIN(low[u], dfn[v]);
    }
    if(u==1&&son_sz>1) cut=1;
    if(!cut) ans[u]=(n-1)*2;
    else ans[u]+=(ll)(n-sum-1)*(sum+1)+n-1;
}
int main() {
    n=read(),m=read();
    while(m--){
        int a=read(),b=read();
        add_edge(a,b);
        add_edge(b,a);
    }
    tarjan(1);
    for(int i=1;i<=n;++i) printf("%lld
", ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/santiego/p/11228783.html