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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章