(好题) Codeforces 19E&BZOJ 4424 Fairy
作者:互联网
日常自闭(菜鸡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