给定一个 个点 条边的无向连通图(无重边自环)和一个 的 ,每个点的度数至少为 。你需要找到以下两个东西的其中一个:
- 一条有 个节点的链;
- 个环,满足每个环的大小 且不是 的倍数,且每个环都有至少一个节点在这些环中仅出现一次;
考虑 dfs 树,有一个结论是若一棵树的最大深度(根节点深度为 ) ,则一定有至少 个叶子。
若叶子个数小于 ,那么由于所有叶子的深度都 ,所以所有叶子的深度之和一定 ,这显然做不到,因为一个点会至少贡献 。
那么考虑在 dfs 树最大深度 选出 个环,保证每个叶子至多仅出现一次。由于每个点的度数都 且无重边,所以设某个叶子 的深度为 ,其中一条返祖边连接的点深度为 ,另一条返祖边深度为 ,且 ,则:
- 有三个包含 和这两个祖先的环,长度分别为:、、;
- 前两个不能选当且仅当 即 ,而这时第三个恰好能选;
- 所以这三个环中一定有一个环满足条件;
那么求一次 dfs 树即可。
时间复杂度 。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const int S=1000005;
int n,m,k;
int esum,to[S],nxt[S],h[S];
bool vis[S],f[S];
int dep[S],fat[S];
inline void add(int x,int y)
{
to[++esum]=y;
nxt[esum]=h[x];
h[x]=esum;
}
void dfs(int u)
{
vis[u]=true;
f[u]=true;
for(int i=h[u];i;i=nxt[i])
{
int v=to[i];
if(!vis[v]) f[u]=false,dep[v]=dep[u]+1,fat[v]=u,dfs(v);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
int tk=k;
k=n/k+!!(n%k);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dep[1]=1;
dfs(1);
int mx=0;
for(int i=1;i<=n;i++) if(dep[i]>dep[mx]) mx=i;
if(dep[mx]>=k)
{
puts("PATH");
printf("%d\n",k);
for(int i=1;i<=k;i++) printf("%d ",mx),mx=fat[mx];
printf("\n");
}
else
{
puts("CYCLES");
for(int u=1;u<=n&&tk>0;u++)
{
if(f[u])
{
tk--;
int a=0,b=0;
for(int i=h[u];i;i=nxt[i])
{
int v=to[i];
if(v==fat[u]) continue;
if(a==0) a=v;
else b=v;
}
if(dep[a]<dep[b]) swap(a,b);
if((dep[u]-dep[a]+1)%3!=0)
{
printf("%d\n",dep[u]-dep[a]+1);
int x=u;
while(x!=a) printf("%d ",x),x=fat[x];
printf("%d\n",a);
}
else if((dep[u]-dep[b]+1)%3!=0)
{
printf("%d\n",dep[u]-dep[b]+1);
int x=u;
while(x!=b) printf("%d ",x),x=fat[x];
printf("%d\n",b);
}
else
{
printf("%d\n",dep[a]-dep[b]+2);
int x=a;
while(x!=b) printf("%d ",x),x=fat[x];
printf("%d %d\n",b,u);
}
}
}
}
return 0;
}