codeforces #628 div2 F题(DFS树/找环/独立集)

题目链接

题目大意

       输入一个由 n n n个顶点和 m m m条边组成的无向图,找一个长度 ≥ n \ge \sqrt n n 的环或者是顶点数为 n \sqrt n n 的独立集(独立集中任意两个点没有直接边)。

分析过程

       此题用 D F S DFS DFS树求解,所谓 D F S DFS DFS树就是将图进行 D F S DFS DFS遍历之后导出的树,如下图所示。(粗线为树边,浅线为非树边)在这里插入图片描述
        D F S DFS DFS树有一个重要的性质,不在同一颗子树上的两点之间没有边(非树边只有可能是在同一条树链上),这个用反证法的思路和 D F S DFS DFS的性质能够容易看出。
        D F S DFS DFS树找出的环不一定是最大环也不一定是最小环。比如下面这组数据。(找最小环需要用 F l o y d Floyd Floyd
在这里插入图片描述

  • 这组数据的最大环是124563最外面这个环, D F S DFS DFS树中找不到这个环。
  • 回到这道题目上面来,在 D F S DFS DFS树上遇到环时判断对应非树边的两个端点差 d e p [ u ] − d e p [ v ] + 1 > = d dep[u]-dep[v]+1>=d dep[u]dep[v]+1>=d是否成立,如果成立则直接输出这个环。( d f s dfs dfs的过程中开堆栈存一下同条树链上已经访问的历史节点)
  • 否则, D F S DFS DFS树同一条树链上的高度差为 d − 1 d-1 d1的顶点之间必然没有环,因此可以用 m o d    ( d − 1 ) \mod (d-1) mod(d1)的方式划分等价类,所有同余的顶点两两之间必然没有边,即可以组成独立集。由鸽巢原理,一共有 n n n个顶点,划分成 d − 1 d-1 d1个等价类,必然至少有一个等价类中的元素个数 ≥ \ge d。

这道题也是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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值