P3469 [POI2008]BLO-Blockade(tarjan求割点)

题目地址

这道题很明显是要寻找割点。

如果一个点x不是割点,那么删去它之后只会使<x,除x外的其他点>,<除x外的其他点,x>这2*(n-1)个有序对不连通。

如果x是割点,设si是x的子节点,subtree(si)为以si为根的搜索子树,size[si]为这个子树包含的节点数,t为使x为割点的子节点的个数,那么删去x后,<subtree(si),除subtree(si)的其他点>,<x,除x外的其他点>,<x的所有祖先节点,subtree(x)>这些有序对不连同,那么ans[x]=$sum ^{t}_{k=1}sizeleft[ s_{k} ight] left( n-sizeleft[ s_{k} ight] ight)$ + 1 * (n - 1) + $left( n-1-sum ^{t}_{k=1}sizeleft[ s_{k} ight] ight)$ + $left( 1+sum ^{t}_{k=1}sizeleft[ s_{k} ight] ight)$

如何判断割点?tarjan算法,如果一个点u,与他的一个子节点v,满足dfn[u] ≤ low[v],那么u就是割点。简单来说,就是点u的有一个子树,子树中所有点能到达的点的最小时间戳都大于等于u的时间戳,说明这个子树中没有点能通过一条边跳过u到达这个子树外的其他点。

要解决这道题,不妨在tarjan深搜的时候顺便算出子树的size,从而算出答案。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 100010
#define MAXM 500010
using namespace std;
typedef long long LL;
int n,m;
int to[MAXM*2],nxt[MAXM*2],head[MAXN],tot=1;
int dfn[MAXN],low[MAXN],size[MAXN],cut[MAXN],num=0;
LL ans[MAXN];

void add(int u,int v)
{
    to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}

void tarjan(int u)
{
    dfn[u]=low[u]=++num;
    size[u]=1;
    int flag=0,sum=0;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
            size[u]+=size[v];
            if(dfn[u]<=low[v])
            {
                flag++;
                ans[u]+=(LL)size[v]*(n-size[v]);
                sum+=size[v];
                if(u!=1||flag>1) cut[u]=true;
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
    if(cut[u]) ans[u]+=(LL)(n-sum-1)*(sum+1)+(n-1);
    else ans[u]=2*(n-1);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;i++) printf("%lld
",ans[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/BakaCirno/p/11510717.html