https://codeforces.com/gym/102055/problem/B
宇宙中有两个阵营,分别为光明和黑暗,现在有n个骑士,每个骑士都能选择加入黑暗或者光明的阵营,加入后的能力值分别为D和L,已知有m对骑士不愿意在同一个阵营,请问如何分配,能使得能力最高的骑士和能力最低的骑士之间的能力差值最小?
对于差值尽量小的问题,可以采用枚举最大值,然后使得最小值尽量大。
此题关键点便是枚举最大值,找到最大的最小值,更新答案。
首先对骑士进行二分图判定,并把他们看成一个连通块,可以知道这个连通块里的最大值mx
和最小值mn
有两种方案(一部分在光明,一部分在黑暗,也可以反过来),记录下这两种方案。
将这些方案按照最大值从小到大排序。
从左到右依次枚举最大值 curmx
。
假设当前位置最大值为curmx
,位置为i
。
如果[1,i-1]
中的连通块个数等于二分图缩点判定后的个数,那么就可以找mn
的最小值curmn
。更新下答案 ans = min(ans, curmx-curmn)
。
但是有个地方要注意,可能得出的curmn
是当前方案i
的孪生方案中的mn
(每个块会有两种方案),因为每个连通块只能选一个方案,所以必须把前面这个孪生方案的mx
的贡献给去掉。这样求出来的curmn
就是正确的。
枚举完这个i
后,应该更新当前位置的mn
,同时如果前面有孪生方案j
的话,那么两个位置都需要更新一下,更新的值便是max(a[j].mn, a[i].mn)
。因为要让最小值尽量大。
上述的步骤可以用数据结构在logn
时间内完成。
总复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
#include
using namespace std;
#define FOR0(a,b) for(int i = a; i < b; ++i)
#define FORE(a,b) for(int i = a; i <= b; ++i)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2e5+5;
const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
int color[maxn], n, m, val[maxn][2], sum[maxn];
int _mn[maxn<<3], pos[maxn];
bool vis[maxn];
struct node {
int mx,mn;
int id;
bool operator < (const node& rhs) const {
return mx < rhs.mx;
}
}a[maxn*2];
void pushup(int rt) {
_mn[rt] = min(_mn[rt<<1], _mn[rt<<1|1]);
}
void build(int l, int r, int rt) {
_mn[rt] = INF;
if(l == r) {
return;
}
int mid = (l+r) >> 1;
build(lson);
build(rson);
pushup(rt);
}
void update(int L, int v, int l, int r, int rt) {
if(l == r) {
_mn[rt] = v;
return;
}
int mid = (l+r) >> 1;
if(mid >= L)
update(L,v,lson);
if(mid < L)
update(L,v,rson);
pushup(rt);
}
int query(int L, int R, int l, int r, int rt) {
if(l <= L && R <= r) {
return _mn[rt];
} else
return INF;
int mid = (l+r) >> 1;
int res = min(query(L,R,lson),query(L,R,rson));
return res;
}
void init() {
for(int i = 0; i <= n; ++i) {
G[i].clear();
pos[i] = vis[i] = color[i] = 0;
}
}
bool flag;
void dfs(int u, node& c1, node& c2) {
if(!flag) return;
c1.mx = max(c1.mx, val[u][0]);
c1.mn = min(c1.mn, val[u][0]);
c2.mx = max(c2.mx, val[u][1]);
c2.mn = min(c2.mn, val[u][1]);
for(auto v: G[u]) {
if(color[v] == 0) {
color[v] = 3-color[u];
dfs(v,c2,c1);
} else if(color[v] == color[u]) {
flag = false;
return;
}
}
}
int main() {
int T;
scanf("%d", &T);
int ca = 0;
while(T--) {
printf("Case %d: ",++ca);
init();
scanf("%d%d", &n, &m);
int u,v;
for(int i = 0; i < m; ++i) {
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 1; i <= n; ++i)
scanf("%d%d", &val[i][0], &val[i][1]);
int sz = 1;
flag = true;
int cnt = 0;
for(int i = 1; i <= n; ++i) {
if(!color[i]) {
cnt++;
a[sz].mx = -INF; a[sz].mn = INF; a[sz].id = i;
a[sz+1] = a[sz];
color[i] = 1;
dfs(i, a[sz], a[sz+1]);
sz += 2;
}
}
if(!flag) {
puts("IMPOSSIBLE");
continue;
}
sort(a+1,a+sz);
// for(int i = 1; i < sz; ++i) {
// cout << a[i].id <<" " << a[i].mx <<" " << a[i].mn << endl;
// }
for(int i = 1; i < sz; ++i) {
sum[i] = sum[i-1];
if(!vis[a[i].id]) {
sum[i]++;
vis[a[i].id]= 1;
}
}
build(1,sz-1,1);
int ans = INF;
for(int i = 1; i < sz; ++i) {
int curmx = a[i].mx, curmn = a[i].mn;
if(sum[i] == cnt) {
if(pos[a[i].id]) {
update(pos[a[i].id], INF, 1,sz-1,1);
}
curmn = min(curmn, query(1,i-1, 1,sz-1,1));
ans = min(ans, curmx-curmn);
}
if(pos[a[i].id]) {
int ml = max(a[i].mn, a[pos[a[i].id]].mn);
update(pos[a[i].id], ml, 1,sz-1,1);
update(i, ml, 1,sz-1,1);
} else {
update(i,a[i].mn,1,sz-1,1);
pos[a[i].id] = i;
}
}
printf("%d\n", ans);
}
return 0;
}