CF1364D(20-10-14)

CF1364D

codeforce 1364D Ehab’s Last Corollary

思想

  • 这一题要点是证明无论如何解都存在
  • 假如这个图没有环,是一个森林,那么将所有点黑白染色,则颜色多的数量一定大于等于⌈n/2⌉,也就大于等于⌈k/2⌉,符合条件1
  • 假如这个图有环,那么一定可以找到一个”最小环“,这个最小环的定义是:环中任何两点除了环上的边,没有其他直连边。找到后,如果这个环中的结点数小于等于k,则符合条件2。如果这个环结点数大于k,那么对这个环间隔取点,取出来的点大于等于⌈k/2⌉,符合条件1
  • 算法简介:逐个读边,利用并查集来判断有没有环,假如读完第x条边后发现有环,那么跑一边dfs来提取这个环。此时这个环一定是最小的,但是边还没读完,所以用后面的边来缩小这个环,这样贪心得到的环一定满足“最小环”

代码

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define N 500110
#define M 1010
#define ll long long
using namespace std;
int n,m,k,top,sum,edge,ss;
int f[N],way[N],wout[N],to[N],nxt[N];
int cnt[2],mk[N];
bool book[N];
queue <int> que;
void init()
{
    for(int i=1;i<=n;i++) f[i]=i;
}
int getf(int x)
{
    if(f[x]==x) return x;
    else f[x]=getf(f[x]);
    return f[x];
}
void add(int &s,int &e)
{
    to[++edge]=e;
    nxt[edge]=wout[s];
    wout[s]=edge;
}
bool dfs(int x,int fa)
{
    if(book[x]) return true;
    book[x]=true;
    sum++;
    for(int i=wout[x];i;i=nxt[i])
    {
        if(to[i]!=fa && dfs(to[i],x))
        {
            way[x]=to[i];
            return true;
        }
    }
    book[x]=false;
    sum--;
    return false;
}
void cal(int x,int fa,int col)
{
    mk[x]=col;
    cnt[col]++;
    book[x]=true;
    for(int i=wout[x];i;i=nxt[i])
    {
        if(to[i]!=fa)
            cal(to[i],x,col^1);
    }   
}
int main()
{

    int a,b,fa,fb;
	scanf("%d%d%d",&n,&m,&k);
    init();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
        fa=getf(a);
        fb=getf(b);
        if(fa==fb)
        {
            top=i;
            dfs(a,0);
            break;
        }
        f[fa]=fb;
    }
    if(!top)
    {
        printf("1\n");
        for(int i=1;i<=n;i++)
            if(!book[i])
                cal(i,0,1);
        int l=cnt[1]>cnt[0];
        for(int i=1;i<=n;i++)
            if(mk[i]==l && top<(k+1)/2)
                printf("%d ",i),top++;
        printf("\n");
    }
    else
    {
        for(int i=top+1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            if(book[a] && book[b])
            {
                for(int j=way[a];j!=b;j=way[j])
                {
                    book[j]=false;
                    sum--;
                }
                way[a]=b;
            }
        }
        if(sum<=k)
        {
            printf("2\n");
            printf("%d\n",sum);
            for(int i=1;i<=n;i++)
                if(book[i]) 
                {
                    ss=i;
                    break;
                }
            for(int i=ss;;i=way[i])
            {
                printf("%d ",i);
                if(way[i]==ss)
                {
                    printf("\n");
                    break;
                }
            }
        }
        else
        {
            printf("1\n");
            for(int i=1;i<=n;i++)
                if(book[i]) 
                {
                    ss=i;
                    break;
                }
            for(int i=1,j=ss;i<=(k+1)/2;i++,j=way[way[j]])
                printf("%d ",j);
            printf("\n");
        }  
    }
	return 0;
}
/*

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值