1. 程式人生 > 其它 >【洛谷】P7687 [CEOI2005] Critical Network Lines (tarjan)

【洛谷】P7687 [CEOI2005] Critical Network Lines (tarjan)

連結

P7687 [CEOI2005] Critical Network Lines

分析

1 由本題對關鍵路徑的定義,首先能想到它首先是滿足割邊(橋)的定義的。
因此我們可以先找出所有的橋,答案是這個集合的一個子集。

2 進一步地,如果當前這條是橋的邊滿足:它所連線的的兩側 至少有一側沒有兩種種類的點,那麼如果我們去掉這條邊,兩側就不再相連通,缺少點的種類的那一側無法訪問缺的這種種類的點,於是把它加進答案。

依據此思路可以先寫tarjan找出所有的橋,再寫一個dfs函式統計當前位置下兩種點的個數,一邊判斷。

然而會超時(tle \(\times\) 1)

優化

判斷的時候需要在原集合中進行查詢。我們於是要對這個查詢進行優化。
仔細觀察(對於無向連通圖的)tarjan和dfs函式,它們的結構驚人的相似,因此我們可以就寫一個tarjan函式,邊統計邊判斷當前的橋是否滿足題目要求

code

#include<bits/stdc++.h>      
//luogu p7687 先找到所有的橋,再判斷 橋的兩邊是否只有<=1種 若是才留下 
#define ll long long      
using namespace std;
const int N = 100005;
int n,m,A,B;
int a[N],b[N];
bool aa[N],bb[N];
vector<int>g[N],v[N];
bool in[N],vis[N];
int cnt,t,e;
int x[10*N],y[10*N];
int rd[N],cd[N];
int dfn[N],low[N],sta[N],f[N];
vector<pair<int,int>>ans,final;
int dp[N][2];  //0:none  1:A   2:B   3:A+B 

void tarjan(int now,int fa){ //無向圖 
	dfn[now]=low[now]=++cnt;
	dp[now][0]+=aa[now];//
	dp[now][1]+=bb[now];//
	for(int i=0;i<g[now].size();i++){
		int x=g[now][i];
		if(!dfn[x]){
			tarjan(x,now);
			dp[now][0]+=dp[x][0];//
			dp[now][1]+=dp[x][1];//
			low[now]=min(low[now],low[x]);
			if(low[x]>dfn[now]){
				if(dp[x][0]==A||dp[x][0]==0||dp[x][1]==B||dp[x][1]==0){
					ans.push_back(make_pair(x,now));
				}
			}
		}
		else{
			if(fa!=x){ 
				low[now]=min(low[now],dfn[x]);
			}
		}
	}
	
} 

int main(){
	scanf("%d%d%d%d",&n,&m,&A,&B);
	for(int i=1;i<=A;i++) scanf("%d",&a[i]),aa[a[i]]=1;
	for(int i=1;i<=B;i++) scanf("%d",&b[i]),bb[b[i]]=1;
	
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x[i],&y[i]);
		g[x[i]].push_back(y[i]);
		g[y[i]].push_back(x[i]);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tarjan(i,i);
		}
	}
	dfs(1,1);
	printf("%d\n",ans.size());
	for(auto i:ans){
		printf("%d %d\n",i.first,i.second);
	}
	return 0;
}