Balance of the Force 【二分图+枚举】

传送们

废话:队友告诉我题意后,我第一反应是二分答案+2-sat,问了下数据范围2e5....,芽儿呦边都建不了。。。。训练赛过程中也没有把这道题A掉,队友其实已经想到了解法的一部分。

解题思路:首先我们肯定得判断答案是否有解,即对冲突条件建边,然后判断是否为二分图。
如果有解:对于每一个连通块来讲,存在两种方案分配(因为存在冲突条件限制),但是两种方案不能同时选择,答案要求我们选择方案中极差最小。我们先把每种方案的最值求出来,并记录这个方案所在的连通块。
最终答案我们可以通过枚举最大值,找寻最小值来更新。我们把方案按照最大值升序排列。
从左到右依次枚举最大值,如果当前所有联通的均存在方案,我们就可以更新答案了。
查询最小值可以通过各种数据来维护(我写了线段树和map的版本,题目时间给的很多,什么方法都可以搞)。
坑点:如果枚举方案的孪生方案(对立的方案)已经出现过了,我们需要消除这个方案的影响。

代码:

#include<bits/stdc++.h>
#define lson sign << 1, l, mid
#define rson sign << 1 | 1, mid + 1, r
#define lowbit(x) (x&(-x))
typedef long long ll;
using namespace std;
const ll mod = 998244353;
const int INF = 0x3f3f3f3f3f3f;
const int maxn = 2e5 + 5;
 
int n, m, val[maxn][2];
int color[maxn], flag, vis[maxn];
vector<int>load[maxn], peo;
struct node {
    int down, up, id;
    bool friend operator<(node a, node b) {
        return a.up < b.up;
    }
} sit[maxn << 1];
int cnt, pre[maxn];
void dfs(int s, int c) {
    if(!flag)
        return ;
    color[s] = c;
    peo.push_back(s);
    for(int i = 0, e; i < load[s].size(); i++) {
        e = load[s][i];
        if(color[e] != -1) {
            if(color[e] != color[s] ^ 1)
                flag = 0;
        } else
            dfs(e, c ^ 1);
    }
}
int minn[maxn << 3];
void update(int sign, int l, int r, int pos, int x) {
    if(l == r) {
        minn[sign] = x;
        return ;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)
        update(lson, pos, x);
    else
        update(rson, pos, x);
    minn[sign] = min(minn[sign << 1], minn[sign << 1 | 1]);
}
int query(int sign, int l, int r, int a, int b) {
    if(l == a && r == b)
        return minn[sign];
    int mid = (l + r) >> 1;
    if(b <= mid)
        return query(lson, a, b);
    else if(a > mid)
        return query(rson, a, b);
    else
        return min(query(lson, a, mid), query(rson, mid + 1, b));
}
bool judge() {
    for(int i = 1; i <= n && flag; i++) {
        if(color[i] == -1) {
            int id = cnt / 2 + 1;
            peo.clear();
            dfs(i, 0);
            sit[++cnt] = node{INF, 0, id};
            for(int &p : peo) {
                sit[cnt].down = min(sit[cnt].down, val[p][color[p]]);
                sit[cnt].up = max(sit[cnt].up, val[p][color[p]]);
                color[p] ^= 1;
            }
            sit[++cnt] = node{INF, 0, id};
            for(int &p : peo) {
                sit[cnt].down = min(sit[cnt].down, val[p][color[p]]);
                sit[cnt].up = max(sit[cnt].up, val[p][color[p]]);
            }
        }
    }
    return flag;
}
void init() {
    flag = 1, cnt = 0;
    for(int i = 1; i <= n; i++) {
        load[i].clear();
        pre[i] = color[i] = -1;
    }
    return ;
}
int main() {
    int T, x, y;
    scanf("%d", &T);
    for(int cas = 1; cas <= T; cas++) {
        scanf("%d %d", &n, &m);
        init();
        for(int i = 1; i <= m; i++) {
            scanf("%d %d", &x, &y);
            load[x].push_back(y);
            load[y].push_back(x);
        }
        for(int i = 1; i <= n; i++)
            scanf("%d %d", &val[i][0], &val[i][1]);
        printf("Case %d: ", cas);
        int ans = INF;
        if(judge()) {
            int now = 0;
            sort(sit + 1, sit + 1 + cnt);
            for(int i = 1; i <= cnt; i++) {
                int id = sit[i].id;
                if(pre[id] == -1) {
                    update(1, 1, cnt, i, sit[i].down);
                    pre[id] = i;
                    now++;
                } else {
                    if(sit[pre[id]].down > sit[i].down) {
                        update(1, 1, cnt, i, INF);
                    } else {
                        update(1, 1, cnt, i, sit[i].down);
                        update(1, 1, cnt, pre[id], INF);
                    }
                }
                if(now == cnt / 2)
                    ans = min(ans, sit[i].up - min(query(1, 1, cnt, 1, i), sit[i].down));
            }
            printf("%d\n", ans);
        } else {
            printf("IMPOSSIBLE\n");
        }
    }
    return 0;
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值