JZOJ 2110【GDKOI2011模拟二】朋友分组

description

有n个人,编号为1~n,告诉你哪些人之间是不友好的。现在,让你将这n个人分成两组,使得每一组之内的人是互相友好的,如果可以分成两组,则输出如何分组的,如果不可以分成两组,那么,输出“IMPOSSIBLE”。

input

第一行两个整数n和m(1<=n<=50,000,0<=m<=500,000),分别表示人数以及不友好的人的对数。以下m行每行两个数a和b,表示a与b是不友好的。

output

如果可以分成两个组,则输出一个方案,第一行为第一组的人的编号,第二行为第二组人的,按升序输出。
若有多组方案,取第一组人数最多的方案。在第一组人数最多的情况下,取字典序最小的方案。其中字典序最小指两组数字拼起来后的序列字典序最小。
如果不能分成两组,输出“IMPOSSIBLE”。

sample

input

5 4
1 4
1 5
2 4
2 5

output

1 2 3
4 5

solution

显然若存在奇环,则无解

要求字典序最小,则优先选小的放在集合1

从小到大枚举,若没被遍历过则放入集合1

接着 d f s dfs dfs,将所有与其相连的放入相反的集合

遂解

code

//#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
#define inf 1000000007
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int n,m,tot,ans[N],dis[N];
bool bz[N],tag;
struct edge{
	int nxt,to,st;
}t[N*2];
int read(){
	int res=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-'&&((ch=getchar())>='0'&&(ch)<='9')) f=-1;
		else ch=getchar();
	}
	while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
	return res*f;
}
void add(int u,int v){
	t[++tot].nxt=t[u].st;t[tot].to=v,t[u].st=tot;
}
void dfs(int x,int fa){
	bz[x]=1;
	for(int i=t[x].st;i;i=t[i].nxt){
		int v=t[i].to;
		if(v==fa)continue;
		if(bz[v]&&(dis[x]+1-dis[v])&1){
			tag=1;
			return;
		}
		if(bz[v])continue;
		dis[v]=dis[x]+1;
		dfs(v,x);
		if(tag)return;
	}
}
void solve(int x){
	for(int i=t[x].st;i;i=t[i].nxt){
		int v=t[i].to;
		if(ans[v]!=-1)continue;
		ans[v]=ans[x]^1;
		solve(v);
	}
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),m=read();
	fo(i,1,m){
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	fo(i,1,n){
		if(!bz[i])dfs(i,0);
	}
	if(tag){
		puts("IMPOSSIBLE");
		return 0;
	}
	fo(i,1,n)ans[i]=-1;
	fo(i,1,n){
		if(ans[i]==-1)ans[i]=0;
		solve(i);
	}
	fo(i,1,n)if(ans[i]==0)printf("%d ",i);
	puts("");
	fo(i,1,n)if(ans[i]==1)printf("%d ",i);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值