1. 程式人生 > 其它 >P3469 [POI2008]BLO-Blockade 題解

P3469 [POI2008]BLO-Blockade 題解

題目大意

P3469 [POI2008]BLO-Blockade

給出一張無向圖,要求輸出分別刪除某個點相連的邊後,無向圖中有多少個有序點對滿足\(x\)\(y\)不連通

問題求解

刪掉一個點是否連通,自然而然就想到了割點,如果這個點是割點,那麼刪掉邊後其他\(n-1\)的點都是連通的,由於是有序點對,所以這個點的答案就是\(2 \times (n-1)\)

對於不是割點的點,需要求出刪除後各連通塊的大小,相乘的和就是答案,然後思考如何快速求出,在搜尋樹上,節點\(i\)的子節點集合中,有\(t\)個點\(s_1,s_2,...,s_k\)滿足割點判定條件,\(dfn[i]≤low[s_k]\)

,於是,刪除\(i\)關聯的所有邊後,無向圖會分成至多\(t+2\)個連通塊因為

  • 節點\(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;
}