洛谷 P3469 [POI2008]BLO-Blockade

思路

这个题有点吓人呢先在这里默认大家都会tarjan找割点了哈

如果你跑 t a r j a n tarjan tarjan找割点然后把割点删掉跑 b f s bfs bfs找连通块,这个 O ( n 2 ) O(n^2) O(n2)显然 T T T飞了

所以我们要找一个边 t a r j a n tarjan tarjan边计算的方法

首先如果一个点在一个连通块内,他的贡献仅仅是所有点和他的联系断了,所以割点的 a n s ans ans就是

2 × ( n − 1 ) 2\times (n-1) 2×(n1)

难的是考虑割点,不过我们首先要知道:

一个连通块的贡献 = = =这个连通块的大小 × ( \times ( ×(整个图大小 − - 这个连通块大小 ) ) )

这是一个通式,适用于以上所有的贡献求解,因为你的这个连通块和外面所有点都失去联系了,固然失去联系数就这么算了

对于一个割点 u u u,它的子树(以 v v v为根节点)中不能连接到割点的祖先的,这样的子树就被割成一个连通块,这些子树的贡献是

∑ d f n [ u ] ≤ l o w [ v ] s i z e [ v ] × ( n − s i z e [ v ] ) \sum_{dfn[u]\leq low[v]}size[v]\times (n-size[v]) dfn[u]low[v]size[v]×(nsize[v])

这样我们可以边 t a r j a n tarjan tarjan边记录了,除去割点和被他割掉的子树就是一个连通块的,它们的贡献是

( n − s u m − 1 ) × ( s u m + 1 ) (n-sum-1)\times (sum+1) (nsum1)×(sum+1)

其中

s u m = ∑ d f n [ u ] ≤ l o w [ v ] s i z e [ v ] sum=\sum_{dfn[u]\leq low[v]}size[v] sum=dfn[u]low[v]size[v]

割点自己的贡献是 n − 1 n-1 n1,合起来,割点的 a n s ans ans的计算公式为

a n s = ∑ d f n [ u ] ≤ l o w [ v ] s i z e [ v ] × ( n − s i z e [ v ] ) + ( n − s u m − 1 ) × ( s u m + 1 ) + ( n − 1 ) ans=\sum_{dfn[u]\leq low[v]}size[v]\times (n-size[v])+(n-sum-1)\times (sum+1)+(n-1) ans=dfn[u]low[v]size[v]×(nsize[v])+(nsum1)×(sum+1)+(n1)

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
const int orz=1000100;
struct EDGE{
    int ver,dis,nxt,pre,num;
}edge[orz];
int head[orz],n,m,cnt;
int ans[orz],dfn[orz],low[orz],dfs_cnt,size[orz];
inline int read(){
    int sym=0,res=0;char ch=0;
    while (ch<'0'||ch>'9')sym|=(ch=='-'),ch=getchar();
    while (ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return sym?-res:res;
}
void file(){
    freopen("read.in","r",stdin);
    freopen("write.out","w",stdout);
}
void add(int u,int v){
    edge[++cnt].ver=v;
    edge[cnt].nxt=head[u];
    head[u]=cnt;
}
void tarjan(int u){
    dfn[u]=low[u]=++dfs_cnt;size[u]=1;
    int sum=0,child=0;bool cut=false;
    for (int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].ver;
        if (!dfn[v]){
            tarjan(v);size[u]+=size[v];
            low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]){
                ans[u]+=size[v]*(n-size[v]);//累计分出去的儿子的贡献
                sum+=size[v];child++;
                if (u!=1||child>=2)cut=true;
            }
        }else{
            low[u]=min(low[u],dfn[v]);
        }
    }
    if (!cut)ans[u]=2*(n-1);
    else ans[u]+=(n-sum-1)*(sum+1)+(n-1);
    //外界是一个连通块,算上他们的贡献,然后自己是一个连通块,加上自己的贡献;
    //每个连通块的贡献都是该块size乘n-size
}
signed main(){
    n=read();m=read();
    for (int i=1;i<=m;i++){
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    tarjan(1);
    for (int i=1;i<=n;i++){
        printf("%lld\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值