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 <bits/stdc++.h>
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,