[CF538H] Summer Dichotomy 题解

Description

\(T\) 名学生,你要从中选出至少 \(t\) 人,并将选出的人分成两组,可以有某一组是空的。

\(n\) 名老师,每名老师要被分配到两个小组之一,对于第 \(i\) 名老师,要求所在的小组中的学生人数 \(\in [l_i, r_i]\)

此外,有 \(m\) 对老师不能在同一个小组中。

你需要判断能否满足所有要求,如果可以,请给出一种方案。

\(t \le T \le 10^9,n,m \le 10^5\)

Sol

首先给出一个结论:在不考虑 \(T,t\) 限制时,当两组人数为 \(\max_{i=1}^n l_i\)\(\min_{i=1}^n r_i\) 时,区间最大。

证明如下:当存在三个老师的 \([l, r]\) 两两无交时,肯定是无解的。

剩下的情况假设二值为 \(n_1, n_2\),我们再分两种情况讨论:

  • \(n_1 \ge n_2\),容易发现所有老师都可以选任意一组加人。

  • \(n_1 < n_2\),容易发现当 \(n_1\) 减小或 \(n_2\) 增大时,都有老师的选择范围受到限制,所以当前区间最优。

考虑加人 \(T,t\) 的限制,因为 \(n_1\) 只能增大,\(n_2\) 只能减小,那么我们只需从 \(n_1\)\(n_2\) 上面增减即可。

判断方案用二分图染色即可。

时间复杂度 \(\mathcal{O}(n+m)\)

Code

#include<bits/stdc++.h>
using namespace std;
inline int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int first[500005], nxt[500005], to[500005], tot;
void Add(int x, int y) {nxt[++tot] = first[x]; first[x] = tot; to[tot] = y;}
int T, t, n, m, l[100005], r[100005], col[100005], mxl, mnr = 1E9;
void dfs(int u, int Col) {
    if(col[u]) {
        if(col[u] != Col) {
            puts("IMPOSSIBLE");
            exit(0);
        }
        return ;
    }
    col[u] = Col;
    for(int e = first[u]; e; e = nxt[e]) {
        int v = to[e];
        dfs(v, 3 - Col);
    }
}
signed main() {
    t = Read(), T = Read();
    n = Read(); m = Read();
    for(int i = 1; i <= n; i++)
        l[i] = Read(), r[i] = Read(), mxl = max(mxl, l[i]), mnr = min(mnr, r[i]);
    if(mxl + mnr > T)  mnr -= (mxl + mnr - T);
    if(mxl + mnr < t)  mxl += (t - mxl - mnr);
    if(mxl < 0 || mnr < 0)  return puts("IMPOSSIBLE"), 0;
    for(int i = 1; i <= m; i++) {
        int x = Read(), y = Read();
        Add(x, y); Add(y, x);
    }
    for(int i = 1; i <= n; i++) {
        int flag1 = (l[i] <= mxl && mxl <= r[i]), flag2 = (l[i] <= mnr && mnr <= r[i]);
        if(!flag1 && !flag2)  return puts("IMPOSSIBLE"), 0;
        if(flag1 && !flag2)  dfs(i, 1);
        if(flag2 && !flag1)  dfs(i, 2);
    }
    for(int i = 1; i <= n; i++)
        if(!col[i])  dfs(i, 1);
    puts("POSSIBLE");
    cout << mxl << " " << mnr << endl;
    for(int i = 1; i <= n; i++)
        printf("%d", col[i]);
    return 0;
}
posted @ 2020-11-12 19:16  verjun  阅读(119)  评论(0编辑  收藏  举报