P3469 [POI2008]BLO-Blockade 題解
阿新 • • 發佈:2021-08-17
題目大意
給出一張無向圖,要求輸出分別刪除某個點相連的邊後,無向圖中有多少個有序點對滿足\(x\)和\(y\)不連通
問題求解
刪掉一個點是否連通,自然而然就想到了割點,如果這個點是割點,那麼刪掉邊後其他\(n-1\)的點都是連通的,由於是有序點對,所以這個點的答案就是\(2 \times (n-1)\)
對於不是割點的點,需要求出刪除後各連通塊的大小,相乘的和就是答案,然後思考如何快速求出,在搜尋樹上,節點\(i\)的子節點集合中,有\(t\)個點\(s_1,s_2,...,s_k\)滿足割點判定條件,\(dfn[i]≤low[s_k]\)
-
節點\(i\)自身是一個連通塊
-
有\(t\)個連通快,分別由搜尋樹上以\(s_k(1≤k≤t)\)為根的子數中的節點構成
-
還可能有一個連通塊,除了由上述節點之外的所有節點構成
所以記下搜尋樹中子樹的大小\(size[x]\),顯然答案就表示成
\[\sum_{i=1}^t{size[s_i]\times(n-size[s_i])}+1\times(n-1)+(n-1-\sum_{i=1}^t{size[s_k]})\times(1+\sum_{i=1}^t{size[s_k]}) \]程式碼實現
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=100005,maxe=1000005; int lnk[maxn],nxt[maxe],son[maxe],cnt=1,num; int dfn[maxn],low[maxn],size[maxn]; int N,M; LL Ans[maxn]; bool cut[maxn]; inline int read(){ int ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();} while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar(); return ret*f; } inline void add_e(int x,int y){son[++cnt]=y;nxt[cnt]=lnk[x];lnk[x]=cnt;} void tarjan(int x){ dfn[x]=low[x]=++num;size[x]=1; int flg=0,sum=0; for(int j=lnk[x];j;j=nxt[j]){ if(!dfn[son[j]]){ tarjan(son[j]); size[x]+=size[son[j]]; low[x]=min(low[x],low[son[j]]); if(dfn[x]<=low[son[j]]){ flg++; Ans[x]+=(LL)size[son[j]]*(N-size[son[j]]); sum+=size[son[j]]; if(x!=1||flg>1)cut[x]=1; } } else low[x]=min(low[x],dfn[son[j]]); } if(cut[x]) Ans[x]+=(LL)(N-sum-1)*(sum+1)+(N-1); else Ans[x]=2*(N-1); } int main(){ freopen("P3469.in","r",stdin); freopen("P3469.out","w",stdout); N=read();M=read(); for(int i=1;i<=M;i++){ int x=read(),y=read(); if(x==y)continue; add_e(x,y);add_e(y,x); } tarjan(1); for(int i=1;i<=N;i++) printf("%lld\n",Ans[i]); return 0; }