Codeforces 538H

100 篇文章 0 订阅
16 篇文章 0 订阅

先特判掉全部划分到一个集合的情况,接下来只讨论存在两个集合的。
我们取一个右端点最小的区间为 A A A,一个左端点最大的区间为 B B B A A A B B B可能相同)。
我们的限制可以分为两部分。第一部分是划分的两个集合各自人数的交非空,注意到当存在三个互不相交的区间是显然无解,否则如果存在互不相交的区间, A A A B B B一定不相交,于是会分在不同集合,那么可以确定一些其他区间的分法(与 A A A B B B在同一集合),剩余不能确定的会和 A A A B B B都有交,任意划分都可以。第二部分是两个集合人数的交的最小值之和不大于 T T T且最大值之和不小于 t t t。注意到不论 A A A B B B是否划分到同一集合,两个集合人数交的最大值有一个是 A A A的右端点,最小值有一个是 B B B的左端点。我们按右端点从小到大排序后,实质限制是一个前缀中的区间都需要和 A A A在同一个集合(不然另一集合的交的最大值和会过小),最小值同理。
再加上一开始不在同一个集合的限制,我们实质上得到了若干个形如两个点是否在同一个集合的限制,建成图后变为二分图判定,容易用dfs实现。
注意有一个坑点,我们求出来的方案可能会全部划分到一个集合,需要留意实现。时间复杂度 O ( n + m ) \mathcal O(n+m) O(n+m)

#include <bits/stdc++.h>
#define FR first
#define SE second
#define inf 0x3f3f3f3f

using namespace std;

typedef pair<int,int> pr;

vector <pr> e[100005];

inline void addEdge(int x,int y,bool v) {
  e[x].push_back(pr(y,v));
  e[y].push_back(pr(x,v));
} 

int col[100005];
bool v0,v1;

void dfs(int x) {
  if (!col[x]) v0=1; else v1=1;
  for(int i=0;i<e[x].size();i++) {
  	int u=e[x][i].FR,v=e[x][i].SE;
  	if (col[u]==-1) {
  		col[u]=col[x]^v;
  		dfs(u);
	  }
	else if ((col[x]^col[u])!=v) {
		puts("IMPOSSIBLE");
		exit(0);
	}
  }
}

pr num[100005];
int id[100005];

bool cmp1(int x,int y) {
  return num[x].FR<num[y].FR;
}

bool cmp2(int x,int y) {
  return num[x].SE<num[y].SE;
}

int main() {
  int lt,rt,n,m;
  scanf("%d%d%d%d",&lt,&rt,&n,&m);
  for(int i=1;i<=n;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	num[i]=pr(x,y);
  }
  for(int i=1;i<=m;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	addEdge(x,y,1);
  }
  if (!m) {
  	int l=0,r=inf;
  	for(int i=1;i<=n;i++) {
  		l=max(l,num[i].FR);
  		r=min(r,num[i].SE);
	  }
	if (l<=r&&l<=rt&&r>=lt) {
		puts("POSSIBLE");
		printf("%d 0\n",max(l,lt));
		for(int i=1;i<=n;i++) putchar('1');
		printf("\n");
		return 0;
	}
  }
  for(int i=1;i<=n;i++) id[i]=i;
  sort(id+1,id+n+1,cmp2);
  int nr=-1,sz=0;
  for(int i=1;i<=n;i++)
    if (num[id[i]].FR>nr) {
    	sz++;
    	nr=num[id[i]].SE;
	}
  if (sz>2) {
  	puts("IMPOSSIBLE");
  	return 0;
  }
  int id1=1,id2=1;
  for(int i=1;i<=n;i++) {
  	if (num[i].SE<num[id1].SE) id1=i;
  	if (num[i].FR>num[id2].FR) id2=i;
  }
  if (sz==2) {
    assert(id1!=id2);
    for(int i=1;i<=n;i++) {
      if (num[i].FR>num[id1].SE) addEdge(id1,i,1);
      if (num[i].SE<num[id2].FR) addEdge(id2,i,1);
    }
  }
  for(int i=1;i<=n;i++) {
  	if (i!=id1&&num[i].SE+num[id1].SE<lt) addEdge(id1,i,0);
  	if (i!=id2&&num[i].FR+num[id2].FR>rt) addEdge(id2,i,0);
  }
  memset(col,255,sizeof(col));
  for(int i=1;i<=n;i++)
    if (col[i]==-1) {
    	if (!v0) col[i]=0; else col[i]=1;
    	dfs(i);
	}
  if (!v0||!v1) {
  	puts("IMPOSSIBLE");
  	return 0;
  }
  int l1=0,r1=inf,l2=0,r2=inf;
  for(int i=1;i<=n;i++)
    if (!col[i]) {
    	l1=max(l1,num[i].FR);
    	r1=min(r1,num[i].SE);
	}
	else {
		l2=max(l2,num[i].FR);
		r2=min(r2,num[i].SE);
	}
  puts("POSSIBLE");
  if (l1+l2>=lt) printf("%d %d\n",l1,l2);
  else if (l1+r2>=lt) printf("%d %d\n",l1,lt-l1);
  else printf("%d %d\n",lt-r2,r2);
  for(int i=1;i<=n;i++) putchar((!col[i])?'1':'2');
  printf("\n");
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值