HDU 2020 多校第一场 1007 Hunting Monsters

题目链接
游记链接

吐槽

我开场第一个开的题就是这个,误判断以为是个我不会的中档贪心题,结果扔给 djq 他都想了好久,然后我就自闭了 2333

题解

真·hard 神题,给出题人点赞!接下来大概就是把英文题解翻译了一遍吧,顺便补了一点证明。

观察

最初始的贪心部分,相信大家都想到了。我们把怪物分成 A i > B i A_i > B_i A i B i A_i \le B_i 的部分,分别记为集合 S , T S,T ,那么有以下性质:

  1. 最终方案任意 T T 集合的元素必然在 S S 集合之前。
  2. 最终方案 T T 集合的元素是按照 A i A_i 从小到大排序的。这个比较显然,交换一组 A i > A j ( i < j ) A_i>A_j(i<j) 显然不会更劣。
  3. 最终方案 S S 集合中的元素是按照 B i B_i 从大到小排序的。证明方法类似,交换一组 B i < B j ( i < j ) B_i<B_j(i<j) 不会使答案更劣。

于是整道题就被我们分成了几部分:

  1. 对于任意 K K ,计算出在 S S 中选 K K 个怪兽需要花费的最小代价。
  2. 对于任意 K K ,计算出在 T T 中选 K K 个怪兽需要花费的最小代价。
  3. 合并以上两部分的答案。

其中第一部分最难。

第一部分

考虑一个 trival 的 dp,把 S S 按照 B B 从大到小排序, f ( i , j ) f(i,j) 表示当前考虑了最后 i i 个元素,选择 j j 个所需要的最小代价。那么显然有如下 dp 方程:

f ( i , j ) = min { max { f ( i 1 , j 1 ) B i , 0 } + A i , f ( i 1 , j ) } f(i,j)=\min\{\max\{f(i-1,j-1)-B_i,0\}+A_i,f(i-1,j)\}

仔细分析一下这个 dp 方程,我们发现对于一个 i i ,它有四种不同的转移:

  1. f ( i 1 , j ) B i f(i-1,j) \le B_i ,那么对于任意 k i k \ge i ,都有 f ( k , j ) = f ( i 1 , j ) f(k,j)=f(i-1,j) 。因为我们按照 B i B_i 从大到小排序,后面的 B k B_k 只会更大。
  2. f ( i 1 , j ) ( B i , A i ] f(i-1,j) \in (B_i,A_i] ,则 f ( i , j ) = f ( i 1 , j ) f(i,j)=f(i-1,j)
  3. f ( i 1 , j ) > A i f(i-1,j)>A_i f ( i 1 , j ) < f ( i 1 , j 1 ) + A i B i f(i-1,j) < f(i-1,j-1)+A_i-B_i ,则 f ( i , j ) = f ( i 1 , j ) f(i,j)=f(i-1,j)
  4. f ( i 1 , j ) > A i f(i-1,j)>A_i f ( i 1 , j ) f ( i 1 , j 1 ) + A i B i f(i-1,j) \ge f(i-1,j-1)+A_i-B_i ,则 f ( i , j ) = max { 0 , f ( i 1 , j 1 ) B i } + A i f(i,j)=\max\{0,f(i-1,j-1)-B_i\}+A_i
    补充说明:我感觉这里 f ( i 1 , j 1 ) f(i-1,j-1) 是有可能 B i \le B_i 的,但是题解好像并没有这么写(如果有错误请指出 /kel)。但是判断条件确实不需要带 max \max ,因为 f ( i 1 , j 1 ) B i f(i-1,j-1) \le B_i 只会让这个条件必然满足。

引理: j 0 = min { j f ( i , j ) > B i } j_0=\min\{j|f(i,j) > B_i\} ,那么对于任意的 j 0 < j < i j_0 < j < i ,都有 f ( i , j + 1 ) f ( i , j ) f ( i , j ) f ( i , j 1 ) f(i,j+1)-f(i,j) \ge f(i,j)-f(i,j-1) ,并且 f ( i , j 0 + 1 ) f ( i , j 0 ) f ( i , j 0 ) B i f(i,j_0+1)-f(i,j_0) \ge f(i,j_0)-B_i 。即删掉所有 f ( i , j ) B i f(i,j) \le B_i ,并且在开头补一个 B i B_i ,则剩下的东西是个下凸函数。

证明前的准备: 两条显然的不等式, f ( i , j ) f ( i 1 , j ) f(i,j) \le f(i-1,j) f ( i , j ) f ( i , j 1 ) f(i,j) \ge f(i,j-1)

接下来的证明都是博主 yy 的,如果有伪证请指出

证明: 考虑数学归纳, i = 1 i=1 时显然满足。我们首先证明 f ( i , j 0 + 1 ) f ( i , j 0 ) f ( i , j 0 ) B i f(i,j_0+1)-f(i,j_0) \ge f(i,j_0)-B_i ,首先我们有 f ( i , j 0 1 ) = f ( i 1 , j 0 1 ) B i f(i,j_0-1)=f(i-1,j_0-1) \le B_i ,这是因为 f ( i , j 0 1 ) f(i,j_0-1) 不可能从情况 4 转移过来,否则就会 A i \ge A_i 。我们分类讨论一下:

  1. f ( i , j 0 ) = max { 0 , f ( i 1 , j 0 1 ) B i } + A i f(i,j_0)=\max\{0,f(i-1,j_0-1)-B_i\}+A_i f ( i , j 0 + 1 ) = f ( i 1 , j 0 ) B i + A i f(i,j_0+1)=f(i-1,j_0)-B_i+A_i (此时有 f ( i 1 , j 0 ) f ( i , j 0 ) B i f(i-1,j_0) \ge f(i,j_0) \ge B_i ),即他们俩都是通过情况 4 转移过来的。
    由于 f ( i 1 , j 0 1 ) B i f(i-1,j_0-1) \le B_i ,因此 f ( i , j 0 ) = A i f(i,j_0)=A_i ,显然 f ( i , j 0 ) B i f ( i 1 , j 0 ) B i f ( i , j 0 + 1 ) A i f(i,j_0)-B_i \le f(i-1,j_0)-B_i \le f(i,j_0+1)-A_i
  2. f ( i , j 0 ) = f ( i 1 , j 0 ) , f ( i , j 0 + 1 ) = f ( i 1 , j 0 ) B i + A i f(i,j_0)=f(i-1,j_0),f(i,j_0+1)=f(i-1,j_0)-B_i+A_i ,此种情况 f ( i , j 0 + 1 ) f ( i , j 0 ) = A i B i f(i,j_0+1)-f(i,j_0)=A_i-B_i 。根据转移条件,我们有 f ( i , j 0 ) f ( i 1 , j 0 ) < f ( i 1 , j 0 1 ) + A i B i A i f(i,j_0) \le f(i-1,j_0) < f(i-1,j_0-1)+A_i-B_i \le A_i ,因此也成立。
  3. f ( i , j 0 = f ( i 1 , j 0 ) , f ( i , j 0 + 1 ) = f ( i 1 , j 0 + 1 ) f(i,j_0=f(i-1,j_0),f(i,j_0+1)=f(i-1,j_0+1) ,归纳一下,则 f ( i , j 0 + 1 ) f ( i , j 0 ) = f ( i 1 , j 0 + 1 ) f ( i 1 , j 0 ) f ( i 1 , j 0 ) max { B i 1 , f ( i 1 , j 0 1 ) } f ( i , j 0 ) B i f(i,j_0+1)-f(i,j_0)=f(i-1,j_0+1)-f(i-1,j_0) \ge f(i-1,j_0)-\max\{B_{i-1},f(i-1,j_0-1)\} \ge f(i,j_0)-B_i

其实接下来对于任意的 j 0 < j < i j_0 < j < i ,都有 f ( i , j + 1 ) f ( i , j ) f ( i , j ) f ( i , j 1 ) f(i,j+1)-f(i,j) \ge f(i,j)-f(i,j-1) 反而好证一些了。我们考虑一个分界点 t t 使得对于 j t j \ge t 都是从情况 4 转移来的,否则都是从 1,2,3 转移来的。首先对于 j < t j<t f ( i , j ) f ( i , j 1 ) f ( i 1 , j ) f ( i 1 , j 1 ) f ( i 1 , j 1 ) f ( i 1 , j 2 ) f ( i , j 1 ) f ( i , j 2 ) f(i,j)-f(i,j-1) \ge f(i-1,j)-f(i-1,j-1) \ge f(i-1,j-1)-f(i-1,j-2) \ge f(i,j-1)-f(i,j-2) 。注意这里不会有 f ( i 1 , j 2 ) B i 1 f(i-1,j-2) \le B_{i-1} 的情况,否则就会有 f ( i , j 2 ) B i f(i,j-2) \le B_i 了,不属于这里讨论的范围。

对于分界点 t t ,有 f ( i , t ) f ( i , t 1 ) = A i B i f(i,t)-f(i,t-1)=A_i-B_i ,根据转移条件,我们有 f ( i 1 , t 1 ) f ( i 1 , t 2 ) < A i B i f(i-1,t-1)-f(i-1,t-2)<A_i-B_i ,即 f ( i , t 1 ) f ( i , t 2 ) < A i B i f(i,t-1)-f(i,t-2)<A_i-B_i 。类似的,也有 f ( i , t + 1 ) f ( i , t ) = f ( i 1 , t ) f ( i 1 , t 1 ) A i + B i f(i,t+1)-f(i,t)=f(i-1,t)-f(i-1,t-1) \ge A_i+B_i

对于 j > t j>t ,我们有 f ( i , j + 1 ) f ( i , j ) = f ( i 1 , j ) f ( i 1 , j 1 ) f ( i 1 , j 1 ) f ( i 1 , j 2 ) = f ( i , j ) f ( i , j 1 ) f(i,j+1)-f(i,j)=f(i-1,j)-f(i-1,j-1) \ge f(i-1,j-1)-f(i-1,j-2)=f(i,j)-f(i,j-1)

证毕。

因此扔掉 B i \le B_i 的部分,并在最前面补个 B i B_i ,确实是个上凸函数。然后就是比较 naive 的部分了,直接上个优先队列维护差分数组,每次扔掉 B i \le B_i 的部分,并且更新一下第一个值(因为第一个点从 B i 1 B_{i-1} 变成 B i B_i 了)。然后再往优先队列里扔一个 A i B i A_i-B_i (决策分界点的差分)即可。复杂度 O ( n log n ) O(n \log n)

第二部分

这个部分我都在想要不要额外开一个小标题,由于强迫症我还是开了。

由于 A i B i A_i \le B_i ,根据观察,直接选择 K K 个最小的 A i A_i 即可。

第三部分

我们知道了选择 i i 个集合 T T 中元素的最小代价 c i c_i ,即初始能量在 [ c i , c i + 1 ) [c_i,c_{i+1}) 时我们都只能选 i i 个。于是我们就知道了如果初始能量在这段区间中,能量会增加多少,设为 d i d_i 。我们找到所有 j j 表示在 S S 中选 j j 个,并且 i + j i+j 这个个数还没有被计算过的 j j f j c i + 1 + d i 1 f_j \le c_{i+1}+d_i-1 的,那么它需要的初始代价就是 max { c i , f j d i } \max\{c_i,f_j-d_i\}

由于 c i , d i c_i,d_i 显然都单增,因此可以做到线性时间。

综上,我们就得到了一个复杂度 O ( n log n ) O(n \log n) 的做法。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }

const int MAXN = 300005, MOD = 1e9 + 7;
struct Data { int a, b; } S[MAXN], T[MAXN];
priority_queue<int, vector<int>, greater<int> > pq;
int aa[MAXN], bb[MAXN], n, C;
LL f[MAXN], c[MAXN], d[MAXN];
bool cmpa(const Data &a, const Data &b) { return a.a < b.a; }
bool cmpb(const Data &a, const Data &b) { return a.b < b.b; }

int main() {
	for (scanf("%d", &C); C--;) {
		scanf("%d", &n);
		int ns = 0, nt = 0;
		for (int i = 1; i <= n; i++) scanf("%d", aa + i);
		for (int i = 1; i <= n; i++) scanf("%d", bb + i);
		for (int i = 1; i <= n; i++) {
			int a = aa[i], b = bb[i];
			if (a <= b) T[++nt] = Data { a, b };
			else S[++ns] = Data { a, b };
		}
		sort(S + 1, S + 1 + ns, cmpb);
		sort(T + 1, T + 1 + nt, cmpa);
		int cnt = 0;
		LL low = 0;
		for (int i = 1; i <= ns; i++) {
			while (!pq.empty()) {
				int sum = pq.top() + low;
				if (sum > S[i].b) break;
				low = sum;
				f[++cnt] = low;
				pq.pop();
			}
			if (!pq.empty()) {
				int sum = pq.top() + low - S[i].b;
				pq.pop();
				pq.push(sum);
			}
			pq.push(S[i].a - S[i].b);
			low = S[i].b;
		}
		for (; !pq.empty(); pq.pop())
			f[++cnt] = (low += pq.top());
		for (int i = 1; i <= nt; i++) {
			c[i] = max(T[i].a - c[i - 1] - d[i - 1], 0ll) + c[i - 1];
			d[i] = d[i - 1] + T[i].b - T[i].a;
		}
		c[nt + 1] = 1e18;
		LL ans = 0; cnt = 0;
		for (int i = 0, j = 0; i <= nt; i++) {
			if (c[i] == c[i + 1]) continue;
			LL r = c[i + 1] - 1 + d[i];
			while (j < ns && f[j + 1] <= r) ++j;
			for (int k = cnt + 1; k <= i + j; k++) {
				LL t = max(f[k - i] - d[i], c[i]);
				ans = (ans + t % MOD * k) % MOD;
//				printf("%lld ", t);
			}
			cnt = i + j;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WAautomaton/article/details/107527989