4424: Cf19E Fairy

本文探讨了在图论中解决奇环切割问题的有效算法。文章首先介绍了在不含奇环的图中每条边均可被切割的基础情况,随后深入分析了含有奇环的情形下切割策略的制定,并给出了一种确保切割后不产生新的奇环的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果原图没有奇环显然每条边都可以砍
下面讨论有奇环的情况
显然每个奇环都要砍到,所以砍的边一定在这些奇环的交中
然后有个结论,在交中砍边
1:砍的边一定不能在偶环上
2:只要不在偶环上,就是一个合法的方案

证明:
不妨设我们砍的是树边
砍掉之后,设分开的两部分为A,B,如果不考虑这些奇环的非树边,A,B分别是二分图(树是二分图
考虑这些非树边,如果这个图不是二分图,就存在奇环,一定存在一种方案,使得从A中的某个点走,经过某些非树边,最后走回A且路径长为奇数
但这种情况是不可能发生的,因为这些奇环的非树边都是连接A,B的,如果A走这些奇环的非树边,走一步后所在点层数的奇偶性不变,但因为最后要回到A,一定会在A,B之间跳跃最终走偶数条这样的边,因为路径长为奇数,于是一定走了奇数条树边,而走树边,走一步后所在点层数的奇偶性改变,走奇数条后不可能回到A,所以不会发生这种情况
这是没有偶环的时候的分析,如果砍的边在某个偶环上,这些非树边中就会存在某一条连接的两点奇偶性不同,上述的情况就会发生,即出现了一个新的奇环

命题得证

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c;while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 1100000;
const int maxm = 1100000;
const int maxd = 22;

int n,m;
int cir,ok;
int e[maxm][2];
int v[maxm];
int fa[maxn];
int findfa(const int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}

struct edge{int y,nex,i;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y,const int i){a[++len]=(edge){y,fir[x],i};fir[x]=len;}

int dep[maxn],fr[maxn][maxd];
void dfs(const int x)
{
    for(int i=1;i<maxd;i++) fr[x][i]=fr[fr[x][i-1]][i-1];
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fr[x][0])
    {
        dep[y]=dep[x]+1; fr[y][0]=x;
        dfs(y);
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=maxd-1;i>=0;i--) if(dep[x]-dep[y]>=(1<<i))
        x=fr[x][i];
    if(x==y) return x;
    for(int i=maxd-1;i>=0;i--) if(fr[x][i]!=fr[y][i])
        x=fr[x][i],y=fr[y][i];
    return fr[x][0];
}
int flag[maxn],s[maxn];
int ans[maxm],ansn;
void solve(const int x)
{
    s[x]=flag[x];
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=fr[x][0])
    {
        solve(y);
        if(s[y]==ok) ans[++ansn]=a[k].i;
        s[x]+=s[y];
    }
}

int main()
{
    read(n); read(m);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y; read(x); read(y);
        e[i][0]=x,e[i][1]=y;
        if(findfa(x)!=findfa(y)) 
        {
            ins(x,y,i),ins(y,x,i),fa[fa[y]]=fa[x];
            v[i]=1;
        }
    }
    dep[1]=1; dfs(1);

    cir=0; ok=0;
    for(int i=1;i<=m;i++)
    {
        int x=e[i][0],y=e[i][1];
        if(x==y&&cir) return puts("0"),0;
        if(x==y) { cir=i; continue; }

        if(!v[i])
        {
            int ff=LCA(x,y);
            if((dep[y]-dep[ff]+dep[x]-dep[ff])&1) flag[y]--,flag[x]--,flag[ff]+=2;
            else
            {
                v[i]=2;
                ok++;
                flag[x]++,flag[y]++,flag[ff]-=2;
            }
        }
    }
    if(cir)
    {
        if(!ok) printf("1\n%d\n",cir);
        else puts("0");
        return 0;
    }
    if(!ok)
    {
        printf("%d\n",m);
        for(int i=1;i<m;i++) printf("%d ",i);
        printf("%d\n",m);
        return 0;
    }

    solve(1);
    if(ok==1)
    {
        for(int i=1;i<=m;i++) if(v[i]==2) 
        {
            ans[++ansn]=i;
            break;
        }
    }

    sort(ans+1,ans+ansn+1);
    printf("%d\n",ansn);
    for(int i=1;i<ansn;i++) printf("%d ",ans[i]);
    if(ansn) printf("%d\n",ans[ansn]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值