CF538H Summer Dichotomy

题意

  • 有一些同学和\(n\)个老师,要求从中选出至少\(t\)个至多\(T\)学生分成两组
  • 要求第\(i\)名老师所分得的学生人数在\([l_i, r_i]\)的范围内并且每个老师都要分到某一组去
  • 并且有一些老师不能在同一组
  • 构造一组方案

先不考虑\(t, T\)的限制,那么分情况考虑:

  1. 所有区间都有交集
  2. 最后答案将老师分成两个集合,且两集合交集的交集为空

对于情况1,只要在交集里面选两个点就好

对于情况2,考虑选的较小的那个点\(x\),可以发现这个点一定会在所有\(r_i\)的前面,否则另外一个点更大,那这个\(r_i\)就无法分组了,所以这个点一定会满足:

\[x\leq\min_{i=1}^{n}\{r_i\} \]

再考虑较大的那个点\(y\),同样的,这个点一定会所有\(l_i\)的后面,否则同样不满足题意,即:

\[y\geq\max_{i=1}^n\{l_i\} \]

然后再考虑\(t,T\)的限制,同样有两种情况:

  1. \(x+y<t\),由于\(x\)只能减小,所以只能增大\(y\),但增大多可能会不满足区间的限制,所以尽量让\(y\)更小
  2. \(x+y>T\),同理只能最小地减小\(x\)

于是,我们能得到的最终的\(x,y\)一定能满足题意,否则就无解,之后建出二分图跑跑就好了

#include<bits/stdc++.h>
#define For(i, a, b) for(int i = (a), en = (b); i <= en; ++i)
#define Rof(i, a, b) for(int i = (a), en = (b); i >= en; --i)
#define Tra(u, i) for(int i = hd[u]; ~i; i = e[i].net)
#define cst const
#define LL long long
#define DD double
#define LD long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
#define eps 1e-12
#define maxn 100000
using namespace std;

int T, t, n, m, mn = inf, mx = 0, L[maxn + 5], R[maxn + 5], col[maxn + 5];

template <class T>
void read(T &x){
	char ch;
	bool ok;
	for(ok = 0, ch = getchar(); !isdigit(ch); ch = getchar()) if(ch == '-') ok = 1;
	for(x = 0; isdigit(ch); x = x * 10 + ch - '0', ch = getchar());
	if(ok) x = -x;
}

int fa[2 * maxn + 5], to[maxn + 5];
int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}

int main(){
	//freopen("in", "r", stdin);
	read(t); read(T); read(n); read(m);
	For(i, 1, n){
		read(L[i]); read(R[i]);
		mn = min(mn, R[i]);
		mx = max(mx, L[i]);
	}
	if(mn + mx < t) mx = t - mn;
	if(mn + mx > T) mn = T - mx;
	if(mn + mx < t || mn + mx > T || mn < 0){puts("IMPOSSIBLE"); return 0;};
	For(i, 1, 2 * n) fa[i] = i;
	For(i, 1, m){
		int u, v; read(u); read(v);
		if(find(u) == find(v)){puts("IMPOSSIBLE"); return 0;}
		fa[find(n + u)] = find(v);
		fa[find(n + v)] = find(u);
	}
	For(i, 1, n) if(find(n + i) != n + i) to[find(n + i)] = find(i);
	For(i, 1, n) L[find(i)] = max(L[find(i)], L[i]), R[find(i)] = min(R[find(i)], R[i]);
	For(i, 1, n) if(find(i) == i){
		if(L[i] > R[i]){puts("IMPOSSIBLE"); return 0;}
		if(!to[i]){
			//cout << L[i] << " " << R[i] << endl;
			if(mn >= L[i] && mn <= R[i]) col[i] = 1;
			else if(mx >= L[i] && mx <= R[i]) col[i] = 2;
			else{puts("IMPOSSIBLE"); return 0;}
		}
		else{
			if(mn >= L[i] && mn <= R[i] && mx >= L[to[i]] && mx <= R[to[i]]){
				col[i] = 1;
				col[to[i]] = 2;
			}
			else if(mn >= L[to[i]] && mn <= R[to[i]] && mx >= L[i] && mx <= R[i]){
				col[i] = 2;
				col[to[i]] = 1;
			}
			else{puts("IMPOSSIBLE"); return 0;}
		}
	}
	puts("POSSIBLE");
	printf("%d %d\n", mn, mx);
	For(i, 1, n) printf("%d", col[find(i)]);
	return 0;
}
posted @ 2020-10-15 10:19  lprdsb  阅读(102)  评论(0编辑  收藏  举报