ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

(好题) Codeforces 19E&BZOJ 4424 Fairy

2019-05-10 15:44:41  阅读:289  来源: 互联网

标签:删除 返祖 好题 4424 Codeforces int 奇环 长度 偶环


 日常自闭(菜鸡qaq)。不过开心的是看了题解之后1A了。感觉这道题非常好,必须记录一下,一方面理清下思路,一方面感觉自己还没有完全领会到这道题的精髓先记下来以后回想。

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

看到二分图,我们肯定会想到奇环。要删除一条边使得剩下的图变成二分图,那么必定剩下的图也不能存在奇环。所以要删除的边都必须经过所有的奇环。这比较好想,但是还有一个难想一点的条件,要删除的边也不能经过任何的偶环,这是因为如果这条边经过了偶环,哪怕删除了这条边剩下的边也会形成新的奇环。为什么呢?画一下图就发现,假定奇环长度为A,偶环长度为B,他们共享的边长度为C,那么他们能形成新的长度为A+B-2*C长度的环,显然这个数是奇数。

综上,能删除的边满足上诉两个条件。我们得到这样一个算法:对所有奇环的边+1,对所有偶环的边-1,最后边值等于奇环个数的就是满足条件的边。 那么我们可以考虑用差分完成这个事情,用d[x]代表dfs树进入x点的边的差分值。染色的过程差分,染完色之后计算便值即可。

当然这道题还没完,以上的过程我们只考虑了dfs树边的情况,但是返祖边(非树边)呢?我们继续思考:经过上面的思考我们还是只考虑奇环上的返祖边,仔细画图观察:在dfs搜索树上,一个奇环返祖边只会属于一个奇环。根据我们上边的结论:边必须要经过所有奇环,可以得到:奇环返祖边能满足条件当且仅当图上只有一个奇环。

接下来还得注意一个小细节:自环。自环我们可以当中奇环,但是这种奇环没有返祖边,所以我们得特殊处理。

呼呼,到这里我们终于能AC了。

细节详见代码以注释:

#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,odd,rec,num,col[N],vis[N],tag[N],dfn[N];
vector<int> ans;

int cnt=1,head[N],nxt[N<<1],to[N<<1],id[N<<1];
void add_edge(int x,int y,int z) {
    nxt[++cnt]=head[x]; to[cnt]=y; id[cnt]=z; head[x]=cnt;
}

void dfs1(int x,int t,int in) {
    col[x]=t; dfn[x]=++num;
    for (int i=head[x];i;i=nxt[i]) {
        if (i==(in^1)) continue;  //这样能处理重边的情况 
        int y=to[i];
        if (!col[y]) dfs1(y,3-t,i);
        else {
            if (dfn[x]<dfn[y]) continue;  //这是一个细节,dfn[x]比dfn[y]大的才是返祖边 
            if (col[x]==col[y]) {  //奇环 
                odd++; rec=i;
                tag[x]++; tag[y]--;
            } else {  //偶环 
                tag[x]--; tag[y]++;
            }
        }
    }
}

void dfs2(int x,int in) {
    vis[x]=1;
    for (int i=head[x];i;i=nxt[i]) {
        if (i==(in^1)) continue;
        int y=to[i];
        if (!vis[y]) {
            dfs2(y,i);
            tag[x]+=tag[y];  //累计子树差分值得到 x入边值 
        }
    }
    if (tag[x]==odd) ans.push_back(in);  //满足条件的边 
}

int main()
{
    cin>>n>>m;
    for (int i=1;i<=m;i++) {
        int x,y; scanf("%d%d",&x,&y);
        if (x==y) {  //特别处理自环的情况 
            odd++; add_edge(0,0,i); rec=cnt; continue;
        }
        add_edge(x,y,i); add_edge(y,x,i);
    }
    
    for (int i=1;i<=n;i++)
        if (!col[i]) dfs1(i,1,0);  //染色 
    for (int i=1;i<=n;i++)
        if (!vis[i]) dfs2(i,0);  //计算边值 
    
    if (odd==0) {
        cout<<m<<endl;
        for (int i=1;i<=m;i++) printf("%d ",i);
    } else {
        if (odd==1) ans.push_back(rec);  //奇环返祖边 
        cout<<ans.size()<<endl;
        sort(ans.begin(),ans.end());
        for (int i=0;i<ans.size();i++) printf("%d ",id[ans[i]]);
    }
    return 0;
}

 

标签:删除,返祖,好题,4424,Codeforces,int,奇环,长度,偶环
来源: https://www.cnblogs.com/clno1/p/10844666.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有