P12546 [UOI 2025] Convex Array 题解

Description

给定一个长度为 nn 的整数数组 aa

判断是否存在一种元素排列 bb,使得对于每个 2in12 \leq i \leq n-1,都满足条件 bi1+bi+12bib_{i-1} + b_{i+1} \geq 2 \cdot b_i

本题中,每个测试点包含多组输入数据。你需要对每组数据独立求解。

n3×105\sum n\leq 3\times 10^5

Solution

先把 aia_i 排序,那么问题可以看成将 a2,a3,,ana_2,a_3,\ldots,a_n 划分成两个集合,每个集合分别和 a1a_1 构成的序列都是上凸的。

考虑 dp。

fi,b,c,df_{i,b,c,d} 表示扫了前 ii 个数,第一个集合末尾为 aia_iaba_b,第二个集合末尾为 aca_cada_d。直接转移是 O(n4)O(n^4) 的。

注意到 b,cb,c 中至少有一个是 i1i-1,所以状态数变为 O(n3)O(n^3)

不妨设 b=i1b=i-1,那么固定 cc 之后,一定是让 dd 越大越好,所以可以改变状态,fi,cf_{i,c} 表示满足条件的状态 (i,i1,c,d)(i,i-1,c,d) 的最大的 dd

如果 c=i1c=i-1,则一定满足 b=i2b=i-2 或者 d=i2d=i-2,这两种状态同样只需要记录最大的第四者,所以这里只有两种状态,容易分讨转移。

考虑 b=i1b=i-1 怎么转移。

  1. i+1i+1 放在第一个集合:即如果 ai+1+ai12aia_{i+1}+a_{i-1}\geq 2a_i,则转移到 (i+1,i,c,d)(i+1,i,c,d)
  2. i+1i+1 放在第二个集合:如果 ai+1+ad2aca_{i+1}+a_d\geq 2a_c,转移到 (i,i1,i+1,c)(i,i-1,i+1,c)

观察第二种转移,容易想到维护一个 (2acad,c)(2a_c-a_d,c) 的集合,可以用动态开点线段树做,不过也可以用 set 维护一个两维分别递增的集合,只有这个集合内的数是有用的,插入时简单维护即可。而第一种转移的意思就是要不要清空 set,同样容易维护。

时间复杂度:O(nlogn)O(n\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 3e5 + 5;

int n;
int a[kMaxN];
std::set<std::pair<int, int>> st;

void ins(int c, int d) {
if (c == -1 || d == -1) return;
std::pair<int, int> p = {2 * a[c] - a[d], c};
auto it = st.lower_bound({p.first, 0});
for (; it != st.end() && it->second < c;) {
st.erase(it);
it = st.lower_bound({p.first, 0});
}
if (it != st.end() && it->first == p.first && it->second > c) return;
if (it != st.begin()) {
--it;
if (it->second < c) st.emplace(p);
} else {
st.emplace(p);
}
}

int get(int x) {
auto it = st.lower_bound({a[x] + 1, 0});
if (it != st.begin()) return prev(it)->second;
else return -1;
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
st.clear();
std::sort(a + 1, a + 1 + n);
a[0] = 1e18, a[++n] = a[1];
std::sort(a + 1, a + 1 + n);
int d1 = 0, d2 = 0;
for (int i = 2; i <= n - 1; ++i) {
int _d1 = -1, _d2 = -1;
_d2 = std::max(_d2, get(i + 1));
if (a[i + 1] + a[i - 1] < 2 * a[i]) st.clear();
if (~d1) {
if (a[i + 1] + a[i - 2] >= 2 * a[i]) ins(i - 1, d1);
if (a[i + 1] + a[d1] >= 2 * a[i - 1]) _d1 = std::max(_d1, i - 2);
}
if (~d2) {
if (a[i + 1] + a[d2] >= 2 * a[i]) ins(i - 1, i - 2);
if (a[i + 1] + a[i - 2] >= 2 * a[i - 1]) _d1 = std::max(_d1, d2);
}
d1 = _d1, d2 = _d2;
}
std::cout << ((~d1 || ~d2 || st.size()) ? "YES\n" : "NO\n");
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P12546 [UOI 2025] Convex Array 题解
https://sobaliuziao.github.io/2025/05/21/post/aa81517f.html
作者
Egg_laying_master
发布于
2025年5月21日
许可协议