Description
有 T 名学生,你要从中选出至少 t 人,并将选出的人分成两组,可以有某一组是空的。
有 n 名老师,每名老师要被分配到两个小组之一,对于第 i 名老师,要求所在的小组中的学生人数 ∈[li,ri]。
此外,有 m 对老师不能在同一个小组中。
你需要判断能否满足所有要求,如果可以,请给出一种方案。
t≤T≤109,n,m≤105。
Solution
先不考虑人数为 [t,T] 的限制,只考虑怎么取出 n1 和 n2 使答案尽可能合法。
注意到如果 max{li}≤min{ri} 那么 n1 和 n2 取这个区间里的任意一个数均可行。
否则不妨让 n1=min{ri},n2=max{li},则一定有 n1<n2,此时如果让 n1 增大则必然会使 r 最大的那个区间两个组都选不上,而让 n1 减少则一定会让 n1 这边能放的区间变为原来的子集,一定不优。对于 n2 同理,所以此时的 n1,n2 一定是最优解。
现在考虑上 [t,T] 的限制,如果 n1+n2∈[t,T] 则不需要调整。如果 n1+n2>T,注意到 n2 不能减少,所以让 n1 减少,n2 不变最优。如果 n1+n2<t 则让 n1 不变,n2 增大最优。
确定了 n1,n2 后只需要对于每个区间判断其能放到哪个组,如果只能放一个组则已经确定,否则不管它。
然后对于不能放在一个组的两个区间连一条边,跑二分图染色即可。
时间复杂度:O(n+m)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <bits/stdc++.h>
const int kMaxN = 1e5 + 5;
int L, R, n, m; int l[kMaxN], r[kMaxN], col[kMaxN]; bool vis[kMaxN]; std::vector<int> G[kMaxN];
bool dfs(int u) { vis[u] = 1; for (auto v : G[u]) { if (!col[v]) { col[v] = 3 - col[u]; dfs(v); } else if (col[v] == col[u]) { return 0; } } return 1; }
void dickdreamer() { std::cin >> L >> R >> n >> m; int n1 = 1e9, n2 = 0; for (int i = 1; i <= n; ++i) { std::cin >> l[i] >> r[i]; n1 = std::min(n1, r[i]), n2 = std::max(n2, l[i]); } if (n1 + n2 > R) n1 = R - n2; if (n1 + n2 < L) n2 = L - n1; if (n1 < 0 || n2 < 0) return void(std::cout << "IMPOSSIBLE\n"); for (int i = 1; i <= n; ++i) { int o1 = (l[i] <= n1 && n1 <= r[i]), o2 = (l[i] <= n2 && n2 <= r[i]); if (!o1 && !o2) return void(std::cout << "IMPOSSIBLE\n"); if (o1 && !o2) col[i] = 1; else if (!o1 && o2) col[i] = 2; } for (int i = 1; i <= m; ++i) { int u, v; std::cin >> u >> v; G[u].emplace_back(v), G[v].emplace_back(u); } for (int i = 1; i <= n; ++i) { if (!vis[i] && col[i] && !dfs(i)) return void(std::cout << "IMPOSSIBLE\n"); } for (int i = 1; i <= n; ++i) { if (!vis[i] && !col[i]) { col[i] = 1; if (!dfs(i)) return void(std::cout << "IMPOSSIBLE\n"); } } std::cout << "POSSIBLE\n" << n1 << ' ' << n2 << '\n'; for (int i = 1; i <= n; ++i) std::cout << col[i]; }
int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; while (T--) dickdreamer(); return 0; }
|