codeforces #628 div2 F題(DFS樹/找環/獨立集)

題目鏈接

題目大意

       輸入一個由nn個頂點和mm條邊組成的無向圖,找一個長度n\ge \sqrt n的環或者是頂點數爲n\sqrt n的獨立集(獨立集中任意兩個點沒有直接邊)。

分析過程

       此題用DFSDFS樹求解,所謂DFSDFS樹就是將圖進行DFSDFS遍歷之後導出的樹,如下圖所示。(粗線爲樹邊,淺線爲非樹邊)在這裏插入圖片描述
       DFSDFS樹有一個重要的性質,不在同一顆子樹上的兩點之間沒有邊(非樹邊只有可能是在同一條樹鏈上),這個用反證法的思路和DFSDFS的性質能夠容易看出。
       DFSDFS樹找出的環不一定是最大環也不一定是最小環。比如下面這組數據。(找最小環需要用FloydFloyd
在這裏插入圖片描述

  • 這組數據的最大環是124563最外面這個環,DFSDFS樹中找不到這個環。
  • 回到這道題目上面來,在DFSDFS樹上遇到環時判斷對應非樹邊的兩個端點差dep[u]dep[v]+1>=ddep[u]-dep[v]+1>=d是否成立,如果成立則直接輸出這個環。(dfsdfs的過程中開堆棧存一下同條樹鏈上已經訪問的歷史節點)
  • 否則,DFSDFS樹同一條樹鏈上的高度差爲d1d-1的頂點之間必然沒有環,因此可以用mod  (d1)\mod (d-1)的方式劃分等價類,所有同餘的頂點兩兩之間必然沒有邊,即可以組成獨立集。由鴿巢原理,一共有nn個頂點,劃分成d1d-1個等價類,必然至少有一個等價類中的元素個數\ged。

這道題也是DFS樹留個坑。

AC代碼

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
typedef long long ll;
int n, m, d, flag, dep[maxn];
vector<int> G[maxn];
stack<int> s;
void dfs(int cur, int par){
	dep[cur] = dep[par] + 1;
	s.push(cur);
	for(auto temp:G[cur]){
		if(temp == par) continue;
		if(!dep[temp]){
			dfs(temp, cur);
			if(flag) return;
			s.pop();
		}else{
			if(dep[cur] - dep[temp] >= d - 1){ //找到符合條件的環 
				cout<<2<<'\n';
				flag = 1;
				cout<<dep[cur] - dep[temp] + 1<<'\n'<<s.top();
				s.pop();
				while(!s.empty()){
					cout<<' '<<s.top();
					if(s.top() == temp) return;
					s.pop();
				}		
			}
		}
	}
}
void solve(){
	int cnt[maxn];
	memset(cnt, 0, sizeof(cnt));
	d = sqrt(n + 0.5);
	if(n > d * d) ++d;
	dfs(1, 0);
	if(!flag){ //存在獨立集
		cout<<1<<'\n';
		int maxi = 0, p, c = 0; 
		for(int i=1;i<=n;++i){
			cnt[dep[i]%(d-1)]++;
			if(cnt[dep[i]%(d-1)] > maxi){
				maxi = cnt[dep[i]%(d-1)];
				p = dep[i]%(d-1);
			}
		}
		for(int i=1;i<=n;++i){
			if(dep[i] % (d - 1) == p){
				if(c) cout<<' ';
				cout<<i;
				++c;
				if(c >= d) break;
			}
		}
	}
}
int main(){
	int t, i, j, u, v;
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(i=1;i<=m;++i){
		cin>>u>>v;	
		G[u].push_back(v);
		G[v].push_back(u);
	}
	solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章