专栏文章

题解:CF2041J Bottle Arrangement

CF2041J题解参与者 4已保存评论 5

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
5 条
当前快照
1 份
快照标识符
@mipyr30r
此快照首次捕获于
2025/12/03 20:08
3 个月前
此快照最后确认于
2025/12/03 20:08
3 个月前
查看原文

Solution

本篇题解参考了 Gold14526 的题解,但是将他的做法优化到了除排序外线性,并且代码十分简短。
bb 从小到大排序。求出整个序列最靠左的最小值的位置 pp,考虑 bnb_n 放在哪里:
  • 如果放在 pp:剩下的怎么放都合法,若 bn=apb_n=a_p 需要操作一次。
  • 如果放在 [1,p)[1,p)[p,n][p,n] 单调递增,直接将最小的几个数 b1,b2,,bnp+1b_1,b_2,\cdots,b_{n-p+1} 放在 [p,n][p,n] 里面。若 bnp+1=apb_{n-p+1}=a_p 需要操作一次。
  • 如果放在 (p,n](p,n][1,p][1,p] 单调递减,直接将最小的几个数 b1,b2,,bpb_1,b_2,\cdots,b_{p} 放在 [1,p][1,p] 里面。若 bp=apb_{p}=a_p 需要操作一次。
正确性证明:考虑证明放在 [1,p)[1,p) 时的正确性。如果放的不是这些数,设放在 pp 处的是 xx,若 x<apx<a_p,由于对于 [1,p)[1,p) 而言,<ap<a_p 的数是一样的,并不能使答案更小;若 x=apx=a_p,则已经增加了 11 的代价,bnp+1b_{n-p+1}[1,p)[1,p) 中最多比原来减少 11 的代价,且不会使不合法的情况变得合法,也不能使答案更小。
发现这样变为了 a,ba,b 规模都缩小的子问题,枚举三种情况,后两种递归解决即可。可以发现递归解决实际上是在遍历笛卡尔树,建立笛卡尔树即可快速处理。复杂度除排序外线性。

Code

CPP
#include <bits/stdc++.h>
#define REP(i, l, r) for (int i = (l); i <= (r); ++ i)
using namespace std;
const int N = 1e6 + 5;
int n, tp, a[N], b[N], ls[N], rs[N], s[N];
int slv(int p, int L, int R) { // a 序列还剩下 [L,R],p 是 [L,R] 的最小值
	if (L > R) return 0;
	int w = 1e9, t1 = n - (p - L), t2 = n - (R - p);
	if (b[t1] <= a[p]) w = min(w, slv(ls[p], L, p - 1) + (b[t1] == a[p]));
	if (b[t2] <= a[p]) w = min(w, slv(rs[p], p + 1, R) + (b[t2] == a[p]));
	return w; // b[n] 放在 p 只有 l=r 才会进行,不需要特判
}
int main() {
	cin >> n;
	REP(i, 1, n) cin >> a[i];
	REP(i, 1, n) cin >> b[i];
	REP(i, 1, n) {
		int x = 0;
		while (tp && a[s[tp]] > a[i]) x = s[tp --];
		if (tp) rs[s[tp]] = i;
		ls[i] = x, s[++ tp] = i;
	}
	sort(b + 1, b + 1 + n);
	int w = slv(s[1], 1, n); 
	cout << (w < 1e9 ? w : -1) << '\n';
	return 0;
}

评论

5 条评论,欢迎与作者交流。

正在加载评论...