传送们
废话:队友告诉我题意后,我第一反应是二分答案+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;
}