zoukankan      html  css  js  c++  java
  • BZOJ4424: Cf19E Fairy

    BZOJ4424: Cf19E Fairy

    CF19E. Fairy

    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


    题解Here!

    感觉和某个题好像啊:

    BZOJ4025: 二分图

    一个图是二分图当且仅当它没有奇环。
    然后如果一个图已经是二分图,那么删除任意一条边都还是二分图。
    于是我们考虑随便求出一棵生成树。
    然后把非树边都拿出来,求出它们两个点的$LCA$和距离$dis$。
    这样,我们可以尝试一条条把非树边放在树上,看是否会构成奇环,统计奇环条数$flag$。
    以下将构成奇环的非树边叫做矛盾边,不会构成奇环的非树边叫做合法边。
    一条树上边被非树上边$(u,v)$“覆盖”,即这条树上边在$u->v$的树上路径上。
    然后分类讨论:
    • 如果$flag==0$,即没有奇环。

    只要把所有边序号输出即可

    • 如果$flag==1$,即有一个奇环。

    那么我们的一个选择是把该条矛盾边删掉,或者是在该矛盾边$(u,v)$的树上路径上找一些边,满足这些边不被合法边覆盖。

    至于为什么要让其不被合法边覆盖,画个图感性理解一下。。。

    这样可以保证该矛盾边不会与其他非矛盾边再形成奇环。

    • 如果$flag>1$,即有若干个奇环。

    那么我们只能选择删除树上那些,被所有矛盾边覆盖,且不被非矛盾边覆盖的边了。

    但是如何判断呢?

    我们可以利用树上差分的思想,记$delta[x]$为$x$和其父亲构成的边上的差分值,把矛盾边的路径$+1$,非矛盾边的路径上$-1$。

    那么最后树上的点$x$如果满足$delta[x]==flag$,那么它与它的父亲组成的边就是我们所求的。

    注意:图有可能不连通,并且会有重边与自环!

    我考虑了这些,但是在$BZOJ$上光荣$WA$了。。。

    我不服,“上诉”到$codeforces$,然后$AC$。。。

    所以$BZOJ$上的是个什么鬼数据啊。。。我交了几个网上的题解好像还有没过的。。。

    所以这个题就这样吧,有时间再来改。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #define MAXN 1000010
    using namespace std;
    int n,m,c=1,d=0,e=1,flag=0;
    int start,end,ancester,id;
    int head[MAXN],h[MAXN],deep[MAXN],fa[MAXN],father[MAXN],vis[MAXN],delta[MAXN];
    int last[MAXN],ans[MAXN];
    struct Edge{
    	int x,y;
    }edge[MAXN];
    struct Tree{
    	int next,to,id;
    }tree[MAXN<<1],question[MAXN<<1];
    struct Question{
    	int x,y,id,lca;
    }que[MAXN];
    inline int read(){
    	int date=0;char c=0;
    	while(c<'0'||c>'9')c=getchar();
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date;
    }
    int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
    void uniun(int x,int y){x=find(x);y=find(y);if(x!=y)father[y]=x;}
    inline void add(int x,int y,int i){
    	tree[c].to=y;tree[c].id=i;tree[c].next=head[x];head[x]=c++;
    	tree[c].to=x;tree[c].id=i;tree[c].next=head[y];head[y]=c++;
    }
    inline void add_que(int x,int y,int i){
    	question[e].to=y;question[e].id=i;question[e].next=h[x];h[x]=e++;
    	question[e].to=x;question[e].id=i;question[e].next=h[y];h[y]=e++;
    }
    void kruskal(){
    	for(int i=1;i<=n;i++)father[i]=i;
    	for(int i=1;i<=m;i++){
    		if(find(edge[i].x)!=find(edge[i].y)){
    			uniun(edge[i].x,edge[i].y);
    			add(edge[i].x,edge[i].y,i);
    		}
    		else{
    			d++;
    			que[d]=(Question){edge[i].x,edge[i].y,i,0};
    			add_que(edge[i].x,edge[i].y,d);
    		}
    	}
    }
    void LCA(int x){
    	vis[x]=1;
    	for(int i=head[x];i;i=tree[i].next){
    		int v=tree[i].to;
    		if(!vis[v]){
    			deep[v]=deep[x]+1;
    			fa[v]=x;
    			last[v]=tree[i].id;
    			LCA(v);
    			uniun(x,v);
    		}
    	}
    	for(int i=h[x];i;i=question[i].next){
    		int v=question[i].to;
    		if(father[v])que[question[i].id].lca=find(v);
    	}
    }
    void get_sum(int x){
    	int v;
    	for(int i=head[x];i;i=tree[i].next){
    		v=tree[i].to;
    		if(!vis[v]){
    			vis[v]=1;
    			get_sum(v);
    			delta[x]+=delta[v];
    		}
    	}
    }
    void work(){
    	if(!flag){
    		printf("%d
    ",m);
    		for(int i=1;i<=m;i++)printf("%d ",i);
    		printf("
    ");
    	}
    	else if(flag==1){
    		int top=0;
    		for(int i=start;i!=ancester;i=fa[i])if(delta[i]==1)ans[++top]=last[i];
    		for(int i=end;i!=ancester;i=fa[i])if(delta[i]==1)ans[++top]=last[i];
    		ans[++top]=id;
    		sort(ans+1,ans+top+1);
    		printf("%d
    ",top);
    		for(int i=1;i<=top;i++)printf("%d ",ans[i]);
    		printf("
    ");
    	}
    	else{
    		int top=0;
    		for(int i=1;i<=n;i++)if(delta[i]==flag)ans[++top]=last[i];
    		sort(ans+1,ans+top+1);
    		printf("%d
    ",top);
    		for(int i=1;i<=top;i++)printf("%d ",ans[i]);
    		printf("
    ");
    	}
    }
    void init(){
    	int x,y,lca,dis,v;
    	n=read();m=read();
    	for(int i=1;i<=m;i++){edge[i].x=read();edge[i].y=read();}
    	kruskal();
    	for(int i=1;i<=n;i++)father[i]=i;
    	for(int i=1;i<=n;i++)if(!vis[i]){deep[i]=1;LCA(i);}
    	for(int i=1;i<=d;i++){
    		x=que[i].x;y=que[i].y;lca=que[i].lca;v=1;
    		dis=deep[x]+deep[y]-2*deep[lca];
    		if(dis&1)v=-1;
    		else{
    			flag++;
    			start=x;end=y;ancester=lca;id=que[i].id;
    		}
    		delta[x]+=v;delta[y]+=v;delta[lca]-=2*v;
    	}
    	memset(vis,0,sizeof(vis));
    	for(int i=1;i<=n;i++)if(!vis[i]){vis[i]=1;get_sum(i);}
    }
    int main(){
    	init();
    	work();
        return 0;
    }
    
  • 相关阅读:
    Merge Two Sorted Lists
    4Sum
    Letter Combinations of a Phone Number
    3Sum Closest
    3Sum
    Longest Common Prefix
    Roman to Integer
    Integer to Roman
    Container With Most Water
    Regular Expression Matching
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9926069.html
Copyright © 2011-2022 走看看