题目
给你一个n(5<=n<=1e5)个点m(n-1<=m<=2e5)条边的无向图,保证图连通
你可以选择以下两种之一来输出:
记
①输出一个正好为d的独立集,输出这些点,使得两两之间无边
②输出一个长度不小于d的简单环,输出这些点
思路来源
https://www.bilibili.com/video/av96374512 dls的B站讲解
https://codeforces.com/contest/1325/submission/73249861 dls的F题代码
题解
dfs树能用来判环,但可能会错过一些环,也不能保证环是最大环或最小环
但是,对于dfs树上不同的两棵子树内的点,它们之间一定没有边相连,不然就在一棵子树上了
因此,当u搜到已经访问过的v时,u和v一定同一棵子树,可用dep[u]-dep[v]+1确定环长
①若>=d的环可以被找出,则输出环,直接退出
②若未找出,说明对于此时的dfs树,u与距离为d-1的v之间无边,
每相距d-1之间的点无边,可通过深度同余d-1的点来确定独立集
显然,根据鸽巢原理,有,
故,取深度相同的最多的元素中的个元素,来确定最终的独立集即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define pb push_back
int n,m,d,dep[N],par[N],num[N];
bool vis[N];
vector<int>e[N];
void dfs(int u){
vis[u]=1;
for(int v:e[u]){
if(!vis[v]){
par[v]=u;
dep[v]=dep[u]+1;
dfs(v);
}
else{
if(dep[u]-dep[v]+1>=d){
vector<int>cyc;
for(int x=u;x!=v;x=par[x])cyc.pb(x);
cyc.pb(v);
puts("2");
printf("%d\n",(int)(cyc.size()));
for(int x:cyc)printf("%d ",x);
exit(0);
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
d=sqrt(n);if(d*d!=n)d++;
for(int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
e[u].pb(v),e[v].pb(u);
}
dfs(1);
for(int i=1;i<=n;++i){
num[dep[i]%(d-1)]++;
}
int p=max_element(num,num+d-1)-num;
vector<int>ind;
for(int i=1;i<=n;++i){
if(dep[i]%(d-1)==p){
ind.pb(i);
}
}
ind.resize(d);
puts("1");
for(int x:ind){
printf("%d ",x);
}
return 0;
}