题目
https://codeforces.com/gym/102055/problem/B
样例
input
3
3 1
1 2
1 2
3 4
5 6
4 3
1 2
2 3
1 3
1 2
3 4
5 6
7 8
2 0
2 1
3 5
output
Case 1: 3
Case 2: IMPOSSIBLE
Case 3: 1
思路
- 他那个两个人不在一队的限制,可以把有关联的人合在一起讨论(连通块),改一个人,连通块里其他人也一起跟着改,所以可以缩成一个人,而这个合成人的 L 就是一个集合,D也是一个集合。而对我们有用的是集合里的最大最小值,所以就只留下最大最小值。
- 连通块缩点就用带权并查集,发现冲突说明无解。
- 所以问题就简化为了 k 个人(有的人是合成人),各从两对[min, max]中选一个,使得最终所有的 min 中最小的与所有的 max 中最大的之差最小。
- 可以这样求:每个人有两个状态,最初所有人都选 max 更小的,计算出一个答案。之后每次将目前 min 最小的那个人转换状态,更新答案。(有点类似双指针的感觉)什么时候停止呢?直到转换状态后 min 反而变小了为止,或者一共操作了超过 2k 步,也停止。因为一个人最多转换两次状态。
- 这里用优先队列存放当前的人,以那个人目前状态的 min 作为排序依据,再用一个 multiset 来存当前选的情况的所有 max。
- 时间复杂度是 Tnlogn
代码
#include <bits/stdc++.h>
using namespace std;
#define MAXN 200005
struct node {
pair<int, int> v[2];
int status;
node() {
status=0;
v[0] = make_pair(2000000000, -1);
v[1] = make_pair(2000000000, -1);
}
bool operator<(const node &o) const {
return v[status].first > o.v[o.status].first;
}
};
int n, m;
vector<node> a; // <L, D>
vector<node> b;
int fa[MAXN]; // 被合并到的点
int flag[MAXN]; // 0: <L, D> 1: <D, L>
vector<int> graph[MAXN];
void upd(pair<int, int> &xx, const pair<int, int> &pp)
{
xx.first = min(xx.first, pp.first);
xx.second = max(xx.second, pp.second);
}
int Fa(int x)
{
if (fa[x]==x) return x;
int fx = fa[x];
int ff = Fa(fa[x]);
flag[x] ^= flag[fx];
return fa[x] = ff;
}
void solve(int iidd)
{
int failed=0;
scanf("%d%d", &n, &m);
a.resize(n);
for (int i=0; i<n; i++) {
a[i] = node();
fa[i]=i;
graph[i].clear();
flag[i]=0;
}
for (int i=0; i<m; i++) {
int uu, vv;
scanf("%d%d", &uu, &vv);
uu--, vv--;
int fu = Fa(uu);
int fv = Fa(vv);
if (fu==fv) {
if (flag[uu] == flag[vv]) {
failed=1;
}
continue;
}
if (fu<fv) {
fa[fv] = fu;
flag[fv] = flag[uu]^flag[vv]^1;
}
else {
fa[fu] = fv;
flag[fu] = flag[vv]^flag[uu]^1;
}
Fa(uu);
Fa(vv);
}
for (int i=0; i<n; i++) Fa(i);
for (int i=0, xx, yy; i<n; i++) {
scanf("%d%d", &xx, &yy);
upd(a[fa[i]].v[flag[i]], make_pair(xx, xx));
upd(a[fa[i]].v[!flag[i]], make_pair(yy, yy));
}
/*
for (int i=0; i<n; i++) if (fa[i]==i) {
printf("[%d] ", i);
for (int j=0; j<2; j++)
printf("%d %d\t", a[i].v[j].first, a[i].v[j].second);
puts("");
}
*/
if (failed) {
printf("Case %d: IMPOSSIBLE\n", iidd);
return;
}
multiset<int> SR;
priority_queue<node> Q;
b.clear();
for (int i=0; i<n; i++) if (fa[i]==i) {
if (a[i].v[0].second>a[i].v[1].second) swap(a[i].v[0], a[i].v[1]);
b.push_back(a[i]);
Q.push(a[i]);
SR.insert(a[i].v[0].second);
}
int ans=2000000000;
int lstmin=2000000000;
for (int i=1; i<=n*2; i++) {
auto o = Q.top();
int mn = o.v[o.status].first;
int mx = *SR.rbegin();
ans = min(ans, mx-mn);
lstmin = mn;
Q.pop();
SR.erase(SR.find(o.v[o.status].second));
o.status ^= 1;
Q.push(o);
SR.insert(o.v[o.status].second);
o = Q.top();
mn = o.v[o.status].first;
if (lstmin > mn) {
break;
}
}
printf("Case %d: %d\n", iidd, ans);
}
int main()
{
int ttt;
scanf("%d", &ttt);
for (int tt=1; tt<=ttt; tt++) {
solve(tt);
}
return 0;
}
/*
3 4 3 1 2 2 3 1 3 1 2 3 4 5 6 7 8
3
3 1
1 2
1 2
3 4
5 6
4 3
1 2
2 3
1 3
1 2
3 4
5 6
7 8
2 0
2 1
3 5
*/