2023-09-05 14:52:07 solution
找路径很好找,我们随便跑个 dfs 树找个深度 \(\ge \frac{n}{k}\) 的路径输出即可。
可是怎么找 \(k\) 个长度不是 \(3\) 的倍数的环呢?既然我们跑了 dfs 树,那么就没有横叉边,对于叶子节点非树边只有返祖边,然后一看这个很奇怪的条件——保证每个点度数大于等于 3,那岂不是叶子节点可以至少构成两个环?加上两个返祖边构成的环,一共有三个环?我们假设两个祖先分别是 \(u,v\),叶子为 \(x\),那么三个环的大小分别为 \(dep_u-dep_x+1\),\(dep_v-dep_x+1\),\(dep_u-dep_v+2\),显然必然有至少一个不是 \(3\) 的倍数。
又要求每个环至少有一个点在这 \(k\) 个环里只出现一次,我们不能保证我们连上去的返祖边构成的环不包括原来的节点,但是我们可以保证让一个叶子节点只选一个环。上面已经说了每个叶子节点至少可以搞出一个合法环,那有 \(k\) 个吗?
我们如果找不到路径,说明任意的 \(dep_x<\frac{n}{k}\),而每个叶子节点跑到根肯定包含了所有点,也就是 \(\sum_{x\in leaf} dep_x\ge n\)(为链时取等),显然个数不可能小于 \(k\)。
所以这时我们直接找每个叶子节点的两条返祖边然后特判即可。因为每个环的大小必然不超过 \(dep_x\),要输出 \(k\) 个环,那么输出个数不会超过 \(\sum k\times dep_x\le n\),符合题意。
\(Code\)
typedef long long ll;
const int N=250005;
int n,m,k,dep[N],fa[N];
vector<int>f[N];
bool is_leaf[N];
stack<int>S;
void dfs(int x){
S.push(x);
dep[x]=dep[fa[x]]+1;
for(int y:f[x])
if(!dep[y])fa[y]=x,is_leaf[x]=1,dfs(y);
if(dep[x]>=n/k){
puts("PATH");
printf("%d\n",dep[x]);
while(!S.empty())printf("%d ",S.top()),S.pop();
exit(0);
}
S.pop();
}
int main(){
n=read(),m=read(),k=read();
for(int i=1,u,v;i<=m;i++)
f[u=read()].push_back(v=read()),f[v].push_back(u);
dfs(1);
puts("CYCLES");
for(int x=1,w=0;w<k&&x<=n;x++){
if(is_leaf[x])continue;
int u=0,v=0;
for(int y:f[x])
if(y==fa[x])continue;
else if(!u)u=y;
else if(!v)v=y;
else break;
if(dep[u]<dep[v])u^=v^=u^=v;
if((dep[x]-dep[v]+1)%3!=0){
printf("%d\n",dep[x]-dep[v]+1);
for(int o=x;o!=fa[v];o=fa[o])printf("%d ",o);
}else if((dep[x]-dep[u]+1)%3!=0){
printf("%d\n",dep[x]-dep[u]+1);
for(int o=x;o!=fa[u];o=fa[o])printf("%d ",o);
}else{
printf("%d\n",dep[u]-dep[v]+2);
for(int o=u;o!=fa[v];o=fa[o])printf("%d ",o);
printf("%d ",x);
}
puts("");
w++;
}
return 0;
}
标签:dep,题解,CF1103C,返祖,叶子,int,dfs,节点
From: https://www.cnblogs.com/NBest/p/17687005.html