Codeforces 19E Fairy

Description
给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成
一个二分图。


【题目分析】
手玩一下,发现删除的是奇环的交集,而且不在偶环上的点,然后乱搞就可以了。


【代码】

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>

#include <set>
#include <map>
#include <string>
#include <algorithm>
#include <vector>
#include <iostream>
#include <queue>

using namespace std;

#define maxn 1000005
#define inf (0x3f3f3f3f)

int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

int fr[maxn<<1],h[maxn],to[maxn<<1],ne[maxn<<1],en=0,ans[maxn<<1],intree[maxn<<1];
int n,m,f[maxn],dep[maxn],fa[maxn],can[maxn<<1];
int cov[maxn],tag[maxn],vis[maxn<<1],out[maxn];

void addedge(int a,int b)
{
    fr[en]=a;
    to[en]=b;
    ne[en]=h[a];
    h[a]=en++;
}

int gf(int k)
{
    if (f[k]==k) return k;
    else return f[k]=gf(f[k]);
}

void dfs(int k)
{
//  printf("dfs on %d\n",k);
    f[k]=k; vis[k]=1;
    for (int i=h[k];i>=0;i=ne[i])
        if (vis[to[i]])
            ans[i]=ans[i^1]=gf(to[i]);
    for (int i=h[k];i>=0;i=ne[i])
        if (!vis[to[i]])
        {
            fa[to[i]]=i/2+1;
            intree[i]=intree[i^1]=1;
            dep[to[i]]=dep[k]+1;
            dfs(to[i]);
            f[to[i]]=k;
        }
}

int dfs2(int k)
{
    for (int i=h[k];i>=0;i=ne[i])
    if (dep[to[i]]==dep[k]+1)
        cov[k]+=dfs2(to[i]);
    return cov[k];
}

int dfs3(int k)
{
    for (int i=h[k];i>=0;i=ne[i])
    if (dep[to[i]]==dep[k]+1)
        tag[k]+=dfs3(to[i]);
    return tag[k];
}

int main()
{
    memset(h,-1,sizeof h);
    n=read(); m=read();
    for (int i=1;i<=m;++i)
    {
        int a,b;
        a=read();b=read();
        addedge(a,b);addedge(b,a);
    }
    for (int i=1;i<=n;++i)
        if (!vis[i])
        {
            dfs(i);
            f[i]=0;
            fa[i]=-1;
        }
    int cnt=0;
    memset(vis,0,sizeof vis);
    for (int i=0;i<en;i+=2)
        if (!intree[i]&&!vis[i])
        {
            vis[i]=vis[i^1]=1;
            int tmp=dep[fr[i]]-dep[ans[i]]+dep[to[i]]-dep[ans[i]]+1;
            if (tmp%2)
            {
                cov[fr[i]]++;cov[to[i]]++;
                cov[ans[i]]-=2;
                cnt++;
                can[i]=can[i^1]=1;
            }
            else
            {
                tag[fr[i]]++;tag[to[i]]++;
                tag[ans[i]]-=2;
            }
        }
    for (int i=1;i<=n;++i) if (!dep[i]) dfs2(i),dfs3(i);
//  printf("cov :"); for (int i=1;i<=n;++i) printf("%d ",cov[i]); printf("\n");
//  printf("tag :"); for (int i=1;i<=n;++i) printf("%d ",tag[i]); printf("\n");
//  printf("cnt is %d\n",cnt);
    if (!cnt)
    {
        printf("%d\n",m);
        for (int i=1;i<=m;++i) printf("%d%c",i,i==m?'\n':' ');
        return 0;
    }
    else for (int i=0;i<en;i+=2)
    {
        int a=fr[i],b=to[i];
        if (dep[a]>dep[b]) swap(a,b);
        if (intree[i]) {if (cov[b]==cnt&&!tag[b]) out[++out[0]]=i/2+1;}
        else if (cnt==1&&can[i]) out[++out[0]]=i/2+1;
    }
    printf("%d\n",out[0]);
    for (int i=1;i<=out[0];++i)  printf("%d%c",out[i],i==out[0]?'\n':' ');
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值