题目

分析:

如果一个点不是割点,那么将其剪去后,只会产生它自己到其他n-1个点的2*(n-1)个有序点对。

如果一个点是割点,将其剪去后,贡献来源于:

1.它子树中两两产生的点对

2.它所有子树与除了它子树之外的点产生的点对

3.它自己和n-1个点产生的点对

(这里的子树都指的是tarjan遍历出来的搜索树中的子树)

注意不要算重!!

最后说明一下割点的判定法则

一个点是割点只需要子树中有至少1个点满足:

low[ v ] >=dfn[ u ]   即这个点不能到达比u更高的点,所以当u删去后这个点一定会被分裂开,满足割点的定义。

而如果一个点是根节点,还要满足:

有至少2个点满足上述条件:只有这样,断去根才会产生不同的块。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ri register int
#define N 100005
#define M 500005
int dfn[N],siz[N],low[N],tot=0,head[N],nex[M<<1],to[M<<1],cut[N],n,m,Ti=0;
ll ans[N];
void add(int a,int b) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; }
void tarjan(int x)
{
    dfn[x]=low[x]=++Ti; siz[x]=1;//!!记得初始化 
    int fl=0,sum=0; 
    for(ri i=head[x];i;i=nex[i]){
        int v=to[i];
        if(!dfn[v]){
            tarjan(v);
            siz[x]+=siz[v];//类dfs 在搜索完了之后累计siz值 
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x]){
                ans[x]+=(ll) siz[v]*(n-siz[v]);//统计对于x的某一颗子树割掉后 对剩下点产生的影响 
                fl++;
                sum+=siz[v];
                if(x!=1 || fl>1) cut[x]=true;
                //割点判断法则:如果一个点是根节点 当且仅当搜索树上存在至少两个点满足上述条件 
            }
        }
        else low[x]=min(low[x],dfn[v]);
    }
    if(cut[x]) ans[x]+=(ll)(n-sum-1)*(sum+1) + (n-1);//统计剩下点与x的所有子树产生的点对 
    //为什么只加一个n-1呢 是因为之前求子树对其他点产生的点对时已经加过了 
    else ans[x]=2*(n-1);//点对是有序的 所以要*2 
}
int main()
{
    int a,b;
    scanf("%d%d",&n,&m);
    for(ri i=1;i<=m;++i) scanf("%d%d",&a,&b),add(a,b),add(b,a);
    tarjan(1);
    for(ri i=1;i<=n;++i) printf("%lld\n",ans[i]);
}
/*
5 5
1 2
2 3
1 3
3 4
4 5
*/
View Code

 

posted on 2019-10-05 21:27  rua-rua-rua  阅读(170)  评论(0编辑  收藏  举报