• bzoj千题计划229:bzoj4424: Cf19E Fairy


    http://www.lydsy.com/JudgeOnline/problem.php?id=4424

    图是二分图的条件:没有奇环

    所以,如果图不存在奇环,删除任意一条边都可以

    如果存在奇环,

    对于树边来说:

    那么可能可以删除的边一定在所有奇环的交集内

    而且这条边不能在偶环内

    因为如果一条边既是奇环上的一条边,又是偶环上的一条边

    删除这条边后,这个奇环和偶环会合并成一个新的奇环

    所以最终的答案= 奇环的交集-偶环的并集

    对于非树边来说:

    如果只有一个奇环,那么可以删除构成环的这条非树边

     

    树边和非树边的判断:并查集

    奇环交与偶环并:差分,统计树上后缀和

    #include<cmath>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 1000001
    
    int e[N][2],ty[N];
    
    int tot=1;
    int front[N],nxt[N<<1],to[N<<1],id[N<<1];
    
    int F[N];
    
    int maxd;
    int fa[N][21];
    
    int dep[N];
    
    int odd_cir,sum[N];
    
    int ans_tot,ans[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int find(int i) { return F[i]==i ? i : F[i]=find(F[i]); }
    
    void add(int u,int v,int num)
    {
        to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; id[tot]=num;
        to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; id[tot]=num;
    }
    
    void dfs(int u)
    {
        for(int i=1;i<=maxd;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
        int t;
        for(int i=front[u];i;i=nxt[i])
        {
            t=to[i];
            if(t==fa[u][0]) continue;
            dep[t]=dep[u]+1;
            fa[t][0]=u;
            dfs(t);
        }
    }
    
    int cal(int x)
    {
        for(int i=front[x];i;i=nxt[i])
            if(to[i]!=fa[x][0])
            {
                cal(to[i]);
                if(sum[to[i]]==odd_cir) ans[++ans_tot]=id[i];
                sum[x]+=sum[to[i]];
            }
    }
    
    int get_lca(int u,int v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        int cnt=dep[u]-dep[v];
        for(int i=maxd;i>=0;--i)
            if(cnt&(1<<i)) u=fa[u][i];
        if(u==v) return u;
        for(int i=maxd;i>=0;--i)
            if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
        return fa[u][0];
    }
    
    int main()
    {
        int n,m;
        int u,v,fu,fv;
        read(n); read(m);
        for(int i=1;i<=n;++i) F[i]=i;
        for(int i=1;i<=m;++i)
        {
            read(u); read(v);
            fu=find(u); fv=find(v);
            if(fu!=fv)
            {
                ty[i]=1;
                F[fu]=fv;
                add(u,v,i);
            }
            e[i][0]=u; e[i][1]=v;
        }
        maxd=log(n)/log(2);
        for(int i=1;i<=n;++i)
            if(find(i)==i) dep[i]=1,dfs(i);
        int self=0;
        int lca;
        for(int i=1;i<=m;++i)
        {        
            u=e[i][0]; v=e[i][1];
            if(u==v && self) { printf("0"); return 0; }
            if(u==v) { self=i; continue; }
            if(!ty[i])
            {
                lca=get_lca(u,v);
                if((dep[u]-dep[lca]+dep[v]-dep[lca])&1)
                {
                    sum[v]--;
                    sum[u]--;
                    sum[lca]+=2;
                }
                else
                {
                    ty[i]=2;
                    odd_cir++;
                    sum[v]++;
                    sum[u]++;
                    sum[lca]-=2;
                }
            }
        }
        if(self)
        {
            if(!odd_cir) printf("1
    %d",self);
            else printf("0");
            return 0;
        }
        if(!odd_cir)
        {
            printf("%d
    ",m);
            for(int i=1;i<m;++i) printf("%d ",i);
            if(m) printf("%d",m);
            return 0;
        }
        for(int i=1;i<=n;++i)
            if(find(i)==i) cal(i);
        if(odd_cir==1)
            for(int i=1;i<=m;++i)        
                if(ty[i]==2) 
                {
                    ans[++ans_tot]=i;
                    break;
                }
        sort(ans+1,ans+ans_tot+1);
        printf("%d
    ",ans_tot);
        for(int i=1;i<ans_tot;++i) printf("%d ",ans[i]);
        if(ans_tot) printf("%d",ans[ans_tot]);
    }

    4424: Cf19E Fairy

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 740  Solved: 187
    [Submit][Status][Discuss]

    Description

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

    Input

    第 1 行包含两个整数 n,m。分别表示点数和边数。
    第 2 到 m+1 行每行两个数 x,y 表示有一条(x,y)的边。

    Output

    输出第一行一个整数,表示能删除的边的个数。
    接下来一行按照从小到大的顺序输出边的序号。

    Sample Input

    4 4
    1 2
    1 3
    2 4
    3 4

    Sample Output

    4
    1 2 3 4

    HINT

    100%的数据,n,m<=1000000

  • 相关阅读:
    HDU6030 Happy Necklace(推导+矩阵快速幂)
    威尔逊定理总结
    卢卡斯定理总结
    扩展欧几里得总结
    HDU2602 Bone Collector(01背包)
    离散数学--集合论
    Linux超全实用指令大全
    HDU6715 算术(莫比乌斯反演)
    莫比乌斯反演总结
    k8s-基于kubeasz项目二进制部署k8s集群
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8428557.html
Copyright © 2020-2023  润新知