1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——BLO

藍書(演算法競賽進階指南)刷題記錄——BLO

題目大意:給定一張圖,輸出當與點i的相連的邊都被去掉後,有多少個無需點對(x,y)不連通.

這道題其實不難.

首先我們知道,一個點i的所有邊去掉後,這個點i肯定與其它點不相連了,所以不相連的對數先加上n-1.

然後我們發現這個點i若不是割點,那麼對數就只有這麼點.但是如果是割點,我們發現它的獨立子樹(即與點i的父親不在一個聯通塊裡的子樹)與它的父親所在的聯通塊之間會有對數.

我們設一棵獨立子樹的大小為size,則這個獨立子樹會產生的對數為(n-size)*size.

而我們設點i父親所在連通塊的大小為size,我們發現會產生的對數也為(n-size)*size.

其實藍書上的公式跟這個公式是等價的.

而如何求一棵子樹以j為根是不是獨立子樹?我們只要判斷一棵子樹是否有非樹邊連向點i的父親所在連通塊內的節點,即是否滿足\bg_white low[j]<=dfn[i]

.

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000,M=500000;
struct side{
  int y,next;
}e[M*2+9];
struct node{
  LL ans; 
  int dfn,low,size;
  bool cut;
}d[N+9];
int n,m,top=1,num,root=1,lin[N+9];
void ins(int X,int Y){
  e[++top].y=Y;
  e[top].next=lin[X];
  lin[X]=top;
}
void tarjan(int k){
  int flag=0,size=0;
  d[k].size=1LL;
  d[k].ans=LL(n-1);
  d[k].dfn=d[k].low=++num;
  for (int i=lin[k];i;i=e[i].next){
    int y=e[i].y;
    if (!d[y].dfn){
      tarjan(y);
      d[k].low=min(d[y].low,d[k].low);
      d[k].size+=d[y].size;
      if (d[y].low>=d[k].dfn){
        flag++;
        if (k^root||flag>1) d[k].cut=1;
        d[k].ans+=1LL*d[y].size*LL(n-d[y].size);
      }else size+=d[y].size;
    }else d[k].low=min(d[k].low,d[y].dfn);
  }
  size+=LL(n-d[k].size);
  d[k].ans+=1LL*size*LL(n-size);
}
Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y;
  for (int i=1;i<=m;i++){
    scanf("%d%d",&x,&y);
    ins(x,y);ins(y,x);
  }
}
Abigail work(){
  tarjan(1);
}
Abigail outo(){
  for (int i=1;i<=n;i++)
    printf("%lld\n",d[i].ans);
}
int main(){
  into();
  work();
  outo();
  return 0;
}