BZOJ1123BLO Tarjan割点+乘法原理+dfs

BZOJ1123 BLO : Tarjan割点+乘法原理+dfs

Description

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

Input

第1行:N, M (1<=N<=100000, 1<=M<=500000)
第2~M+1行 X Y 表示X与Y中有一条边。

Output

共N行,每行一个正整数代表如果去掉第i个点有多少个不能到大的点对。

Sample Input
5 5
1 2
2 3
1 3
3 4
4 5
Sample Output
8
8
16
14
8
HINT

题解

这一题中蕴含的技巧就是求割点时计算其将图分成了多少个大小为多少的连通块。
因为答案就是这些连通块大小互相乘的和。
关键在于,再求割点时维护一个vis[i]代表搜索树中这个子树的大小。
因为一个割点将图分成的连通块是其下面的所有子树(互不相连)与这个点上面的所有点。tmp表示这个点的子树之前的同父亲子树的和。为什么这么算可以得到答案,可以自己推一推。
不要忘记最后答案要乘2。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#define MAXN 150000+10
#define MAXM 700000+10
#define LL long long
using namespace std;

int head[MAXN],num,n,m;
int dfn[MAXN],low[MAXN],vis[MAXN],dfnum,root,son;
LL ans[MAXN];
struct Edge{
    int from,to,next;
}edge[MAXM*2];
void add(int from,int to)
{
    edge[++num].next=head[from];
    edge[num].from=from;
    edge[num].to=to;
    head[from]=num;
}
void tarjan(int x,int fa)
{   
    LL cnt=0;
    dfn[x]=low[x]=++dfnum;vis[x]=1;
    for(int i=head[x];i;i=edge[i].next)
    {   
        if(edge[i].to==fa) continue;
        if(!dfn[edge[i].to])
        {   
            tarjan(edge[i].to,x);
            vis[x]+=vis[edge[i].to];//记大小
            low[x]=min(low[x],low[edge[i].to]);
            if(low[edge[i].to]>=dfn[x])
            {
                ans[x]+=1LL*cnt*vis[edge[i].to];//加上到edge[i].to这颗子树的方案
                cnt+=1LL*vis[edge[i].to];
            }
        }else if(vis[edge[i].to]) low[x]=min(low[x],dfn[edge[i].to]);
    }
    ans[x]+=1LL*cnt*(n-cnt-1);//加上x上面的点的方案
}
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);
    }
    tarjan(1,0);
    for(int i=1;i<=n;i++)
        printf("%lld\n",1LL*(ans[i]+n-1)*2);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章