[POI2008]BLO-Blockade 割点

[POI2008]BLO-Blockade 割点

题面

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

注意:求完\(u\)的子树中所有割点后,还要加上剩下来的大联通块\((n-sum-1)\times(sum+1)\)和自己\(1\times(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\n", ans[i]);
    return 0;
}
posted @ 2019-07-22 22:01  Santiego  阅读(113)  评论(0编辑  收藏  举报