嗅探器(割點)

cn是大帅哥886發表於2024-07-17

https://www.luogu.com.cn/problem/P5058

第2題 嗅探器 檢視測評資料資訊

某軍搞資訊對抗實戰演習,紅軍成功地侵入了藍軍的內部網路。藍軍共有兩個資訊中心,紅軍計劃在某臺中間伺服器上安裝一個嗅探器,從而能夠偵聽到兩個資訊中心互相交換的所有資訊。

但是藍軍的網路相當的龐大,資料包從一個資訊中心傳到另一個資訊中心可以不止有一條通路。

現在需要你儘快地解決這個問題,應該把嗅探器安裝在哪個中間伺服器上才能保證所有的資料包都能被捕獲?

輸入格式

輸入檔案的第一行一個整數 n,表示藍軍網路中伺服器的數目。

接下來若干行是對藍軍網路的拓撲結構描述,每行是兩個整數i,j表示編號為i和編號為j的兩臺伺服器間存在雙向連線。

伺服器的編號從1開始,一行兩個0表示網路的拓撲結構描述結束,再接下來是兩個整數a,b分別表示兩個中心伺服器的編號。

1≤n≤2×1e5,邊數不超過 5×1e5

輸出格式

輸出滿足條件的伺服器編號。如果有多個解輸出編號最小的一個,如果找不到任何解,輸出 No solution。

輸入/輸出例子1

輸入:

5

2 1

2 5

1 4

5 3

2 3

5 1

0 0

4 2

輸出:

1

樣例解釋

對於判斷A,B兩點的關係(是否在同一連通塊),可從A點dfs,分類討論B點和U,V二點關係(B在U,V上,在U,V中間,在U,V下,這個位置關係用時間戳判斷)

割點好題。

**題意:**無向連通圖中有A、B兩點,問是否存在一個點能切斷A、B之間的聯絡。

那這題就可以分類討論A,B兩點位置,使得A,B兩點不在同一個連通塊,從而找到割點

為了簡化問題,我們直接從 A 點開始做DFS,找另外的B點。

最容易想到的就是判斷b的時間戳(dfn)是否不小於我們找到的割點,如下圖

但是這種想法是有缺陷的。

假如a有一條連向b的邊,但b卻是由u遍歷到的,那麼這時u就不能分開a與b。

所以我們想到利用判斷 u是割點的 v點,並且可以整理一下,分成三類

根據位置關係討論,B在u點v點上方,B在u點v點之間,B在u點v點之下,這些位置關係可以用時間戳判斷。

對於一個割點 u ,若判定 u 為割點的邊是 (u,v),那麼有以下討論,

  • 如果 dfn[b] < dfn[u],則 bu 的祖先或者 bu 不在同一個子樹內,由於 a 是根,那麼 a 和 b一定在一個連通塊內。

  • 如果 dfn[b] > dfn[u] && dfn[b] <dfn[v],則 b 是 v 的兄弟子樹中的節點,無法確定 a,b 是否在一個連通塊。

  • 如果 dfn[b] >= dfn[v],則 b 是 v 的子樹中的節點,a,b 一定不在一個連通塊。

進而,我們只需要第三種情況即可。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;

int n, u1, v1, x, y, dfn[N], low[N], idx=0, root=0;
bool cut[N];
vector<int> a[N]; 
void dfs(int u, int fa)
{
	dfn[u]=low[u]=++idx;
	
	int child=0;
	for (int i=0; i<a[u].size(); i++)
	{
		int v=a[u][i];
		if (!dfn[v])
		{
			child++;
			dfs(v, u);
			
			low[u]=min(low[u], low[v]);
			if (low[v]>=dfn[u] && u!=root && dfn[y]>=dfn[v]) cut[u]=1;
		}
		else low[u]=min(low[u], dfn[v]);
	}
	
//	if (u==root && child>=2) cut[root]=1;
}
int main()
{
	scanf("%d", &n);
	while (scanf("%d%d", &u1, &v1) && u1 && v1)
	{
		a[u1].push_back(v1);
		a[v1].push_back(u1);
	}
	scanf("%d%d", &x, &y);
	
	root=x;
	dfs(root, 0);
	
	for (int i=1; i<=n; i++)
		if (cut[i])
		{
			printf("%d", i);
			return 0;
		}
	
	printf("No solution");
	return 0;
}

相關文章