洛谷P3469 [POI2008]BLO-Blockade 割点

题目链接:https://www.luogu.com.cn/problem/P3469

分两种情况;
情况1:选择的不是割点,说明删除该点后,整张图还是连通的,剩余 n-1 个点还是可以互相访问的,只是少了被删的点访问其他点与其他点访问被删的点,即(n-1)*2 次。
情况2:选择的是割点,删去该点后,整个图会被分成 k 个连通块,访问此时会少Σki (第i个连通块的节点数) 乘 n-ki+ n-1(别忘了被删节点访问其它节点的次数)。
我们用tarjan来判断每个点是否是割点,并再tarjan的过程中,记以每个节点为根的子树大小。如果 low[u] >= dfn[v],说明u不能通过其它路径到达v之前的点,所以v就是割点,当然如果 v 是根的话,只有他有两个不能到达v之前的子树,才是割点,本题特判一下 点1 即可。
代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int maxm=1e6+5;
typedef long long ll;
struct node
{
    int to,next;
}p[maxm];
int head[maxn],dfn[maxn],low[maxn];
ll ans[maxn];
int size[maxn];
int cnt,dfn_num;
int n,m;
bool cut[maxn];//标记是否为割点
void add(int x,int y)
{
    p[++cnt].next=head[x];
    p[cnt].to=y;
    head[x]=cnt;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++dfn_num;
    size[u]=1;//以该点为根的子树大小
    ll sum=0;
    int tag=0;//判断是否是割点
    for(int i=head[u];i;i=p[i].next)
    {
        int v=p[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            size[u]+=size[v];
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])
            {
                sum+=size[v];
                ans[u]+=(ll)size[v]*((ll)n-(ll)size[v]);
                tag++;
                if(tag>1||u!=1)
                cut[u]=1;
            }
        }
        else
        low[u]=min(low[u],dfn[v]);
    }
    if(!cut[u])
    ans[u]=(n-1)*2;
    else
    ans[u]+=(ll)(n-sum-1)*(ll)(sum+1)+(n-1);
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i])
    tarjan(i);
    for(int i=1;i<=n;i++)
    printf("%lld\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/104561157