[题解]luogu_P3469_BLO(理解tarjan/割点

给定一张无向图,求每个点被*之后有多少个有序点对(x,y)(x!=y,1<=x,y<=n)满足x无法到达y

首先不是割点答案为2*(n-1),是割点就要考虑删掉割点会分开哪些连通块

考虑tarjan的过程,核心是对搜索树的处理,如果是割点的话删除掉点x会产生的连通块为x的所有以儿子为根的子树和x子树外的所有部分,每个子树产生的贡献为$size[s1]*(n-size[s1])$,x子树外的贡献为$(size[x])*(n-size[x])$,以及x自己的为$(n-1)$

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100009;
const int maxm=500009;
int n,m,tim,root;
struct node{
    int v,nxt;
}e[maxm<<1];
int head[maxn],cnt=1;
inline void add(int u,int v){
    e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;
}
int dfn[maxn],low[maxn];bool cut[maxn];
ll ans[maxn],sz[maxn];
void tarjan(int x){
    dfn[x]=low[x]=++tim;sz[x]=1;
    int son=0,sum=0;
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].v;
        if(!dfn[y]){
            tarjan(y);sz[x]+=sz[y];
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                son++;
                ans[x]+=sz[y]*(n-sz[y]);//每个点只能到当前子树内的点 
                sum+=sz[y];
                if(x!=root||son>1)cut[x]=1;
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
    if(cut[x])ans[x]+=1ll*(n-sum-1)*(sum+1)+(n-1);//x子树外的不能到x子树内的且x不能到其他所有点 
    else ans[x]=2*(n-1);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);if(u==v)continue;
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)
    if(!dfn[i])root=i,tarjan(i);
    for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
}

 

上一篇:「刷题笔记」Tarjan


下一篇:使用Tarjan进行缩点(有向图)