B. Balance of the Force (思维)

题目

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


*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值