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


链接

P7687 [CEOI2005] Critical Network Lines

分析

1 由本题对关键路径的定义,首先能想到它首先是满足割边(桥)的定义的。
因此我们可以先找出所有的桥,答案是这个集合的一个子集。

2 进一步地,如果当前这条是桥的边满足:它所连接的的两侧 至少有一侧没有两种种类的点,那么如果我们去掉这条边,两侧就不再相连通,缺少点的种类的那一侧无法访问缺的这种种类的点,于是把它加进答案。

依据此思路可以先写tarjan找出所有的桥,再写一个dfs函数统计当前位置下两种点的个数,一边判断。

然而会超时(tle \(\times\) 1)

优化

判断的时候需要在原集合中进行查找。我们于是要对这个查找进行优化。
仔细观察(对于无向连通图的)tarjan和dfs函数,它们的结构惊人的相似,因此我们可以就写一个tarjan函数,边统计边判断当前的桥是否满足题目要求

code

#include      
//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];
vectorg[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>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;idfn[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;
}