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;
}