P3469 [POI2008]BLO-Blockade

题目链接

题解:

这道题很容易看出是求割点的板子题,但是难点是计算贡献。

其实手推一下样例就可以发现,如果一个点不是割点的话那么割掉它的贡献就是n-1,因为少了n-1次拜访。

而如果它是割点怎么办呢?就可以这样做,它的贡献就是它的子树的大小size,子树上有size个点,而断掉它,这size个点的贡献为size*(n-size)。而这个点本身的贡献为n-1,还要算不是它的子树上的点的贡献,一共还剩下n-size-1个点,所以贡献同子树的贡献,也就是(n-size-1)*(size+1)的贡献,最后累加起来求和就可以了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
struct node{
    int nxt,to;
}edge[maxn*2];
int head[maxn],cnt;
void add(int x,int y){
    edge[++cnt].nxt=head[x];
    edge[cnt].to=y;
    head[x]=cnt;
}
int n,m,x,y;
int dfn[maxn],low[maxn],in;
bool check[maxn];
long long ans[maxn];
long long siz[maxn];
void tarjan(int x){
    dfn[x]=low[x]=++in;
    siz[x]=1;
    int son=0;
    long long sum=0;
    bool cut=false;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(!dfn[v]){
            tarjan(v);
            low[x]=min(low[v],low[x]);
            siz[x]+=siz[v];
            if(low[v]>=dfn[x]){
                son++;
                ans[x]+=(long long)siz[v]*(n-siz[v]);
                sum+=siz[v];
                if(x!=1||son>=2) cut=true;
            }
        }
        else low[x]=min(dfn[v],low[x]);
    }
    if(!cut) ans[x]=(n-1)*2;
    else ans[x]+=(n-1)+(n-sum-1)*(sum+1); 
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    tarjan(1);
    for(int i=1;i<=n;i++) printf("%lld
",ans[i]);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/LJB666/p/11651506.html