P1551 亲戚题解

题外话

看了看大家各位大佬的做法,都是用并查集(我根本不会用 QAQ),所以我就在想能不能用其他做法,在经过亿点思考后,我尝试了 dfs。

算法分析

题目大意

给出 $n$ 个点和 $m$ 条边,以及 $q$ 次询问,每次询问 $i,j$ 是否在同一个图中。

思路

首先把图存起来(这里我用了 vector)。然后对于每次询问,从 $i$ 点开始遍历,是否能到达 $j$ 点。但是,这样跑时间复杂度极高:$O(qnm)$,TLE 到自闭。

在上述方法中,我们可以发现:有许多点被遍历了多次,那我们可以把 $n$ 都遍历一次,这样对于每次询问只需要 $O(1)$ 的时间复杂度。

如何遍历呢?我们可以初始化每个人的祖先是自己,对于每次遍历到的两个点,把两人的祖先合并为较小的一个。然后直接遍历 $1 \sim n$,这虽然能 AC(因为数据太弱了,只有 5000),但是时间复杂度仍然高:$O(nm)$。

优化

难道我们一定要遍历 $n$ 个点?

显然不是,在同一个图中,只需要遍历最小的那一个,这样这个图中的所有点的祖先就可以合并,也就是说,只有一个点没有被遍历过才需要遍历。时间复杂度:$O(n)$。

代码参考

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5010;
int n,m,q,a[N];
vector<int> v[N];
void dfs(int x){
	for(auto i:v[x]){
		if(a[i]>a[x]){  //合并为更小的
			a[i]=a[x];
			dfs(i);  //只有没被遍历过才需要搜
		}
	}
	return ;
}
signed main(){
	cin>>n>>m>>q;
	for(int i=1;i<=m;++i){
		int x,y; cin>>x>>y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	for(int i=1;i<=n;++i) a[i]=i;//初始化祖先都是自己
	for(int i=1;i<=n;++i){     //从最小的开始
		if(a[i]==i) dfs(i);   //只有没被遍历过才需要遍历
	}
	while(q--){
		int x,y; cin>>x>>y;
		if(a[x]==a[y]) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
} 
posted @ 2025-07-27 10:13  雨梦-H  阅读(25)  评论(0)    收藏  举报