CF1325F Ehab's Last Theorem(dfs树

题意

求出一个大于 n \lceil\sqrt{n}\rceil n 的环或找出 n \lceil\sqrt{n}\rceil n 个点的独立集。

分析

d f s dfs dfs 树可以找最大环。
d f s dfs dfs 树是什么?就是从某个点进行 d f s dfs dfs 形成的树,如图。

深色的是树边,浅色的是非树边。

d f s dfs dfs 树有个性质:每条非树边 ( a , b ) (a, b) (a,b) 都连向子树中某个点。
那么, a a a b b b 在树上的链与非树边 ( a , b ) (a,b) (a,b) 会形成一个环,环的大小为 d e p [ a ] d e p [ b ] + 1 |dep[a]-dep[b]|+1 dep[a]dep[b]+1
容易看出,最大环一定由某条链和某条非树边组成。
所以我们可以用一个栈维护根到当前点的链,当环的大小超过 n \lceil\sqrt{n} \rceil n 时,就输出。

下面证明如果不存在这种环,就一定存在至少 n \lceil\sqrt{n} \rceil n 个点的独立集
由于不存在这种环,因此每个点最多有 n 2 \lceil\sqrt{n} \rceil -2 n 2 条非树边,也就是说,我们每选择一个点,最多会导致 n 1 \lceil\sqrt{n}\rceil -1 n 1 个点无法被选。所以最少可以选 n \lceil\sqrt{n} \rceil n 个点,按深度从大到小选即可。

代码如下

#include <bits/stdc++.h>
#define N 200005
using namespace std;
typedef long long LL;
struct custom_hash {
    static uint64_t splitmix64(uint64_t x) {
        x += 0x9e3779b97f4a7c15;
        x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
        x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
        return x ^ (x >> 31);
    }
    size_t operator()(uint64_t x) const {
        static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
        return splitmix64(x + FIXED_RANDOM);
    }
};
LL z = 1;
struct node{
	int a, b, n;
}d[N * 2];
int h[N], fa[N], dep[N], tag[N], v[N * 2], cnt = 1, sq;
vector<int> vec, ans;
void cr(int a, int b){
	d[++cnt].a = a; d[cnt].b = b; d[cnt].n = h[a]; h[a] = cnt;
}
void dfs(int a){
	int i, j, b;
	vec.push_back(a);
	for(i = h[a]; i; i = d[i].n){
		if(v[i]) continue;
		v[i] = v[i ^ 1] = 1;
		b = d[i].b;
		if(!dep[b]){
			dep[b] = dep[a] + 1;
			dfs(b);
		}
		else{
			if(dep[a] - dep[b] >= sq - 1){
				printf("2\n%d\n", dep[a] - dep[b] + 1);
				for(j = dep[b] - 1; j < dep[a]; j++){
					printf("%d ", vec[j]);
				}
				exit(0);
			}
		}
	}
	if(!tag[a]){
		tag[a] = 1;
		ans.push_back(a);
		for(i = h[a]; i; i = d[i].n){
			b = d[i].b;
			tag[b] = 1;
		}
	}
	vec.pop_back();
}
int main(){
	int i, j, n, m, a, b;
	scanf("%d%d", &n, &m);
	sq = sqrt(n - 1) + 1;
	for(i = 1; i <= m; i++){
		scanf("%d%d", &a, &b);
		cr(a, b);
		cr(b, a);
	}
	dep[1] = 1;
	dfs(1);
	printf("1\n");
	for(i = 0; i < sq; i++) printf("%d ", ans[i]);
	return 0;
}

各种题解及学习笔记~ 文章被收录于专栏

各种题解及学习笔记

全部评论

相关推荐

bg双非本科,方向是嵌入式。这次秋招一共拿到了&nbsp;8&nbsp;个&nbsp;offer,最高年包&nbsp;40w,中间也有一段在海康的实习经历,还有几次国家级竞赛。写这篇不是想证明什么,只是想把自己走过的这条路,尽量讲清楚一点,给同样背景的人一个参考。一、我一开始也很迷茫刚决定走嵌入式的时候,其实并没有一个特别清晰的规划。网上的信息很零散,有人说一定要懂底层,有人说项目更重要,也有人建议直接转方向。很多时候都是在怀疑:1.自己这种背景到底有没有机会2.现在学的东西到底有没有用3.是不是已经开始晚了这些问题,我当时一个都没答案。二、现在回头看,我主要做对了这几件事第一,方向尽早确定,但不把自己锁死。我比较早就确定了嵌入式这个大方向,但具体做哪一块,是在项目、竞赛和实习中慢慢调整的,而不是一开始就给自己下结论。第二,用项目和竞赛去“证明能力”,而不是堆技术名词。我不会刻意追求学得多全面,而是确保自己参与的每个项目,都能讲清楚:我负责了什么、遇到了什么问题、最后是怎么解决的。第三,尽早接触真实的工程环境。在海康实习的那段时间,对我触动挺大的。我开始意识到,企业更看重的是代码结构、逻辑清晰度,以及你能不能把事情说清楚,而不只是会不会某个知识点。第四,把秋招当成一个需要长期迭代的过程。简历不是一次写完的,面试表现也不是一次就到位的。我会在每次面试后复盘哪些问题没答好,再针对性补。三、我踩过的一些坑现在看也挺典型的:1.一开始在底层细节上纠结太久,投入产出比不高2.做过项目,但前期不会总结,导致面试表达吃亏3.早期有点害怕面试,准备不充分就去投这些弯路走过之后,才慢慢找到节奏。四、给和我背景相似的人一点建议如果你也是双非,准备走嵌入式,我觉得有几件事挺重要的:1.不用等“准备得差不多了”再投2.项目一定要能讲清楚,而不是做完就算3.不要只盯着技术,多关注表达和逻辑很多时候,差的不是能力,而是呈现方式。五、写在最后这篇总结不是标准答案,只是我个人的一次复盘。后面我会陆续把自己在嵌入式学习、竞赛、实习和秋招中的一些真实经验拆开来讲,希望能对后来的人有点帮助。如果你正好也在这条路上,希望你能少走一点弯路。
x_y_z1:蹲个后续
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务