CF1364D Solution
题解
依题目所述,2种完成方法一定可以满足一种,以下为简单证明:若图中存在简单环,设该环长度为\(l\)。若\(l\le k\),则满足条件1;若\(l>k\),间隔取点可以得到包含\(\lfloor \frac{l}{2}\rfloor\)个点的独立集,又因为\(\lfloor \frac{l}{2}\rfloor\ge \lceil \frac{k}{2}\rceil\),满足条件2。如果图中不存在环,则该图为二分图(树),一定满足条件1。
上述证明正可以成为我们的解题思路(出题人伏笔妙a),具体实现:dfs找出图中的一个环,利用栈记录搜索到的全部节点,如果同一节点经过2次则判断为环,递归后回溯则可保证不会出现多余节点。如果没有找到环说明这是一棵树,黑白染色(使节点与其祖先不为同一颜色)后取节点数较多的颜色输出即可。如果找到环的话,仍需注意我们记录的只是节点,其可能构成的不是简单环。为此遍历所有环中的节点,如果其存在一条不在环中的边通向环中其他节点,则将该边另一侧全部节点删去,将此边加入。对于找出的简单环,若\(l>k\)则隔一个点输出一个(条件2),若\(l\le k\)则直接将该环输出(条件1)。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=2e5+10;
int fst[N],nxt[2*M],v[2*M],cnt;
int st[N],top;
int r[N];
bool vis[N],c[N];
void add(int x,int y)
{
v[++cnt]=y;
nxt[cnt]=fst[x]; fst[x]=cnt;
}
bool dfs(int x,int fa)
{
st[++top]=x;
if(vis[x]) return 1;
vis[x]=1;
for(int i=fst[x];i;i=nxt[i])
{
int y=v[i];
if(y==fa) continue;
if(dfs(y,x)) return 1;
}
top--; vis[x]=0;
return 0;
}
void dfs2(int x,int fa)
{
for(int i=fst[x];i;i=nxt[i])
{
int y=v[i];
if(y!=fa) {c[y]=c[x]^1; dfs2(y,x);}
}
}
int main()
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
int x,y,sum=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
if(!dfs(1,0))
{
printf("1\n");
c[1]=0; dfs2(1,0);
for(int i=1;i<=n;i++) sum+=c[i];
k=(k+1)/2;
if(sum>=k)
{
for(int i=1;i<=n && k;i++)
if(c[i]) {printf("%d ",i); k--;}
return 0;
}
for(int i=1;i<=n && k;i++)
if(!c[i]) {printf("%d ",i); k--;}
return 0;
}
int qwq=st[top],tot=0;
while(st[--top]!=qwq) r[st[top]]=st[top+1],c[st[top]]=1;
c[qwq]=1,r[qwq]=st[top+1];
for(int i=1;i<=n;i++)
{
if(!c[i]) continue;
for(int j=fst[i];j;j=nxt[j])
{
int y=v[j],pos=r[y];
if(!c[y] || y==r[i] || r[y]==i) continue;
while(pos!=i) {c[pos]=0; pos=r[pos];}
r[y]=i;
}
}
for(int i=1;i<=n;i++)
{
tot+=c[i];
if(c[i]) qwq=i;
}
if(tot>k)
{
printf("1\n"); k=(k+1)/2;
while(k--) {printf("%d ",qwq); qwq=r[r[qwq]];}
return 0;
}
printf("2\n%d\n%d ",tot,qwq);
int pos=r[qwq];
while(pos!=qwq) {printf("%d ",pos); pos=r[pos];}
return 0;
}