训练3.21(C:Summer Dichotomy)

训练3.21(C:Summer Dichotomy)

cf538H
题目:http://code**forces.com/problemset/problem/538/H

题意:T个学生来报考,至少录取t个学生,且录取了的学生要分成两个班(人数>=0)。
现有n个老师,其中存在m对矛盾,矛盾双方不可在同一班级任职。且对于老师i,若班级人数在 l [ i ] – r [ i ]之间,则该老师可以到该班任职。n位老师都需要分配。

题解:

抛开T和t的影响:若老师i和老师j能在同一个班级任课,那么线段li到ri和线段lj到rj必定有交集。题目要分成两个班,那么就是说将n个老师分成两部分,这两部分各自交集不为空。画一下图可知:若 n1=min(ri),n2=max(li),1<=i<=n,则:班级1的最高人数是n1,班级2的最低人数是n2(若真的可以分成两个符合要求的班级的话)
这里的n1,n2对应代码中的A,B。

考虑T和t的影响:那么就是 t<=n1+n2<=T才是合法的。
所以当n1+n2<t时,人数应该增加,增加就只有n2了;当n1+n2>T时,n1减小,以满足限制。

经上面处理后,两个班级人数确定了,接下来就是老师的分派了。老师的分配有两点限制:
(1)班级人数
(2)老师间矛盾

二分涂色

所以先确定那些只能去1班或者只能去2班的老师,相应的“填上色”,若出现两个班都不能去的老师,那么就输出IMPOSSIBLE并结束,剩下的就是那些可1可2的。

bfs一下检测当前填色是否冲突并给那些被别人间接确定了颜色的老师填上色。最后就是可1可2的老师了,分配1班或2班后,都没矛盾就行。具体见代码。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#include<string>
#define ll long long
using namespace std;
const int maxn=1e5+555;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
vector<int>G[maxn];
int l[maxn],r[maxn];
int vis[maxn];
int dfs(int x,int f){
	for(int i=0;i<G[x].size();i++){
		int u=G[x][i];
		if(u==f)continue;
		if(!vis[u]){
			vis[u]=-vis[x];
			int tmp=dfs(u,x);
			if(tmp==0)return 0;
		}
		else {
			if(vis[u]==vis[x])return 0;
		}
	}
	return 1;
}
vector<int>vc;
int check(int a,int l,int r){
	if(a>=l&&a<=r)return 1;
	return 0;
}
int main(){
	int T,t,n,m,A,B;
	cin>>t>>T>>n>>m;
	A=inf;B=-inf;
	for(int i=1;i<=n;i++){
		scanf("%d%d",&l[i],&r[i]);
		A=min(A,r[i]);
		B=max(B,l[i]);
	}
	if(A+B<t)B=t-A;
	else if(A+B>T)A=T-B;
	
	if(A<0||B<0){
		printf("IMPOSSIBLE\n");return 0;
	}
	int a,b;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);
		G[a].push_back(b);
		G[b].push_back(a);
	}
	
	int out=0;
	for(int i=1;i<=n;i++){
		int f1,f2;
		f1=check(A,l[i],r[i]);
		f2=check(B,l[i],r[i]);
		if(!f1&&!f2){
			out=1;break;
		}
		else if(f1&&!f2)vis[i]=1;
		else if(!f1&&f2)vis[i]=-1;	
	}
	if(out==1){
		printf("IMPOSSIBLE\n");return 0;
	}
	int tmp=1;
	for(int i=1;i<=n;i++){
		if(vis[i])tmp=dfs(i,i);
		if(tmp==0){
			out=1;break;
		}
	}
	if(out==1){
		printf("IMPOSSIBLE\n");return 0;
	}
	tmp=1;
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			vis[i]=1;
			tmp=dfs(i,i);
		}
		if(tmp==0){
			out=1;break;
		}
	}
	if(out==1){
		printf("IMPOSSIBLE\n");return 0;
	}else{
		printf("POSSIBLE\n");
		printf("%d %d\n",A,B);
		for(int i=1;i<=n;i++)printf("%d",vis[i]==-1?2:1);
		printf("\n");
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值