J. Spy
题目链接:
https://codeforces.com/gym/103466/problem/J
大概题意:
一个团队需要一名茶水员和一名代码员,Amy 和 Bob 分别有 n n n 名茶水员和 n n n 名代码员。现在Bob 还没有组好团队,而 Amy 的团队成员已经组好了。问Bob 在知道 Amy 团队成员的情况下,组队可以获得最大的期望值。
大致想法:
通过模拟可以发现,当 Bob 确定好自己的团队成员的时候,每个团队带来的贡献是 Amy 团队所有比他们小的数量。这里的复杂是 O O O( n l g n nlgn nlgn)
那么确定 Bob 这里的团队构成呢,刚开始想了一个很假的贪心,使 Bob 超过 x x x 的数量尽可能多,然后去枚举 x x x 。这里的总复杂度是 O O O( n 3 n^3 n3 l g n lgn lgn) 看起来复杂度是对的 但是这个贪心是不对的。
正确的做法是建图连边,以 B i B_i Bi 代表 Bob 的第 i i i 个茶水员,以 C j C_j Cj 代表Bob的第 j j j 个代码员,先算出 V i j V_{ij} Vij 的权值,这个权值就是团队 Amy 所有小于他们的数量。建完图跑一次最大流,跑出来的答案就是我们最后要求解的答案。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
struct hungarian { // km
int n;
vector<int> matchx; // 左集合对应的匹配点
vector<int> matchy; // 右集合对应的匹配点
vector<int> pre; // 连接右集合的左点
vector<bool> visx; // 拜访数组 左
vector<bool> visy; // 拜访数组 右
vector<T> lx;
vector<T> ly;
vector<vector<T> > g;
vector<T> slack;
T inf;
T res;
queue<int> q;
int org_n;
int org_m;
hungarian(int _n, int _m) {
org_n = _n;
org_m = _m;
n = max(_n, _m);
inf = numeric_limits<T>::max();
res = 0;
g = vector<vector<T> >(n, vector<T>(n));
matchx = vector<int>(n, -1);
matchy = vector<int>(n, -1);
pre = vector<int>(n);
visx = vector<bool>(n);
visy = vector<bool>(n);
lx = vector<T>(n, -inf);
ly = vector<T>(n);
slack = vector<T>(n);
}
void addEdge(int u, int v, int w) {
g[u][v] = max(w, 0); // 负值还不如不匹配 因此设为0不影响
}
bool check(int v) {
visy[v] = true;
if (matchy[v] != -1) {
q.push(matchy[v]);
visx[matchy[v]] = true; // in S
return false;
}
// 找到新的未匹配点 更新匹配点 pre 数组记录着"非匹配边"上与之相连的点
while (v != -1) {
matchy[v] = pre[v];
swap(v, matchx[pre[v]]);
}
return true;
}
void bfs(int i) {
while (!q.empty()) {
q.pop();
}
q.push(i);
visx[i] = true;
while (true) {
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v = 0; v < n; v++) {
if (!visy[v]) {
T delta = lx[u] + ly[v] - g[u][v];
if (slack[v] >= delta) {
pre[v] = u;
if (delta) {
slack[v] = delta;
} else if (check(v)) { // delta=0 代表有机会加入相等子图 找增广路
// 找到就return 重建交错树
return;
}
}
}
}
}
// 没有增广路 修改顶标
T a = inf;
for (int j = 0; j < n; j++) {
if (!visy[j]) {
a = min(a, slack[j]);
}
}
for (int j = 0; j < n; j++) {
if (visx[j]) { // S
lx[j] -= a;
}
if (visy[j]) { // T
ly[j] += a;
} else { // T'
slack[j] -= a;
}
}
for (int j = 0; j < n; j++) {
if (!visy[j] && slack[j] == 0 && check(j)) {
return;
}
}
}
}
void solve() {
// 初始顶标
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
lx[i] = max(lx[i], g[i][j]);
}
}
for (int i = 0; i < n; i++) {
fill(slack.begin(), slack.end(), inf);
fill(visx.begin(), visx.end(), false);
fill(visy.begin(), visy.end(), false);
bfs(i);
}
// custom
for (int i = 0; i < n; i++) {
if (g[i][matchx[i]] > 0) {
res += g[i][matchx[i]];
} else {
matchx[i] = -1;
}
}
cout << res << "\n";
/*
for (int i = 0; i < org_n; i++) {
cout << matchx[i] + 1 << " ";
}
cout << "\n";
*/
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<ll> b(n), c(n);
vector<pair<ll, ll>> a(n);
for (int i = 0; i < n; i++) cin >> a[i].first;
for (int i = 0; i < n; i++) cin >> a[i].second;
for (int i = 0; i < n; i++) cin >> b[i];
for (int i = 0; i < n; i++) cin >> c[i];
sort(a.begin(), a.end());
vector<ll> qz;
vector<int> qzv;
qz.push_back(a[0].first);
qzv.push_back(a[0].second);
for (int i = 1; i < n; i++) {
qz.push_back(a[i].first);
qzv.push_back(a[i].second + qzv.back());
}
hungarian<int> km(n, n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int w = 0;
ll x = b[i] + c[j];
auto it = lower_bound(qz.begin(), qz.end(), x);
if (it != qz.begin()) {
it--;
w = qzv[it - qz.begin()];
}
km.addEdge(i, j, w);
}
}
km.solve();
return 0;
}