[ICPC2017 WF] Money for Nothing

你谷 link

loj link

一道可以作为决策单调性模板题刷的题。

考虑暴力怎么做,其实就是把生产商和消费商两两匹配算一遍答案,时间复杂度 \(\mathcal O\left(n^2\right)\)\(n\)\(m\) 同阶,所以时间复杂度中统一写作 \(n\))。

直接暴力肯定是不行的,得考虑优化,可以先进行第一波基本优化——消去无用决策。

我们可以大力发扬人类智慧,利用生活常识进行剪枝,首先我们可以发现,如果两个生产商进行比较,一个生产商来得又晚,东西又贵,你显然嫌弃他,同理两个消费商中走得早还出钱少肯定也不行,可以考虑排序把这些都去掉。

这样我们现在将生产商和消费商都按时间排好序后剪枝就得到生产商的售价越来越低,消费商的买价也越来越低的两个数组。

然后考虑进一步优化,这里就需要用一点黑科技了,考虑决策单调性优化。

怎么发现决策单调性呢?可以考虑打表发现规律,也可以用四边形不等式,这里博主使用一种最最简单的办法——暴力算。

我们设第 \(i\) 个生产商的到达时间为 \(l_i\),售价为 \(a_i\),第 \(i\) 个消费商的离开时间为 \(r_i\),买价为 \(b_i\),则第 \(i\) 个生产商和第 \(j\) 个消费商组合的收益是 \((r_j-l_i)(b_j-a_i)\)

考虑反证法,设两个消费商 \(i\)\(i'\) 满足 \(l_i<l_{i'}\),两个生产商 \(j\)\(j'\) 满足 \(r_j<r_{j'}\),且满足对 \(i\) 来说和 \(j'\) 组合比和 \(j\) 组合更优,而对 \(i'\) 来说反而是 \(j\) 更优,则满足下面的式子:

\[(r_{j'}-l_i)(b_{j'}-a_i)>(r_j-l_i)(b_j-a_i)\\ (r_{j'}-l_{i'})(b_{j'}-a_{i'})<(r_j-l_{i'})(b_j-a_{i'}) \]

对两个式子进行展开移项得到:

\[r_{j'}b_{j'}-r_jb_j>l_i(b_{j'}-b_j)+a_i(r_{j'}-r_j)\\ r_{j'}b_{j'}-r_jb_j<l_{i'}(b_{j'}-b_j)+a_{i'}(r_{j'}-r_j) \]

合并得:

\[l_i(b_{j'}-b_j)+a_i(r_{j'}-r_j)<l_{i'}(b_{j'}-b_j)+a_{i'}(r_{j'}-r_j) \]

因为满足 \(l_{i'}>l_i\)\(a_{i'}<a_i\)\(r_{i'}>r_i\)\(b_{i'}<b_i\),所以与上式是矛盾的,所以假设错误,确实满足决策单调性。

满足决策单调性以后呢?怎么利用决策单调性优化我们的搜索,也就是在一个较优的时间复杂度内找到每个消费商的最优生产商。

我们考虑可以以一种类似整体二分的方式,用消费商去匹配生产商,利用分治的思想,以及利用之前找到的最优决策点来缩小我们现在的搜索区间,假设我们现在正在寻找 \([l,r]\) 这个区间里每个点的最优决策点,决策点的可能区间为 \([L,R]\),我们可以找到第 \(mid=\left\lfloor\dfrac{l+r}2\right\rfloor\) 个消费商的决策点,我们发现 \([l,mid-1]\) 的最优决策点都在该决策点以左,\([mid+1,r]\) 同理,这样我们只要再递归解决,只需要递归 \(\mathcal O\left(\log n\right)\) 层就能解决,每层搜索的总和是 \(\mathcal O\left(n\right)\) 的,所以总时间复杂度 \(\mathcal O\left(n\log n\right)\)

c++ 代码
#include<bits/stdc++.h>

using namespace std;

#define Reimu inline void // 灵梦赛高
#define Marisa inline int // 魔理沙赛高
#define Sanae inline bool // 早苗赛高

typedef long long LL;
typedef unsigned long long ULL;

typedef pair<int, int> Pii;
typedef tuple<int, int, int> Tiii;
#define fi first
#define se second

const int N = 500010;

int n, m;
LL ans;
Pii a[N], b[N];

inline LL calc(int i, int j) { return 1LL * (a[i].fi - b[j].fi) * (a[i].se - b[j].se); }

Reimu solve(int l, int r, int L, int R) {
	if (l > r) return;
	int mid = l + r >> 1, loc = L;
	if (b[L].fi >= a[mid].fi) return solve(mid + 1, r, L, R);
	for (int i = L + 1; i <= R && b[i].fi < a[mid].fi; ++i) if (calc(mid, i) > calc(mid, loc)) loc = i;
	ans = max(ans, calc(mid, loc));
	solve(l, mid - 1, L, loc); solve(mid + 1, r, loc, R);
}

int main() {
	ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
	cin >> m >> n;
	for (int i = 1; i <= m; ++i) cin >> b[i].se >> b[i].fi;
	for (int i = 1; i <= n; ++i) cin >> a[i].se >> a[i].fi;
	sort(a + 1, a + n + 1, greater<>()); sort(b + 1, b + m + 1);
	int _ = 1; for (int i = 2; i <= m; ++i) if (b[i].se < b[_].se) b[++_] = b[i]; m = _;
	_ = 1; for (int i = 2; i <= n; ++i) if (a[i].se > a[_].se) a[++_] = a[i]; reverse(a + 1, a + (n = _) + 1);
	b[0].se = INT_MAX; solve(1, n, 1, m);
	cout << ans;
	return 0;
}
posted @ 2022-04-29 14:33  老莽莽穿一切  阅读(181)  评论(0编辑  收藏  举报