题解:P12546 [UOI 2025] Convex Array
补充了部分时间复杂度与状态复杂度的表述。
% 你赛出了这题,赛后补题参照FFTotoro的题解详细化了一下具体转移过程及思路,在此感谢原作者(怎么套娃了)。
不难得出题意等价于找出两个不同的序列使得相邻两数差单调不降,两个序列的并集为原序列集合(可重集),两个序列的交集为升序排序后的 \(\{a_1\}\)。类似一个下凸包的形式,其中最低点就是 \(a_1\)。
考虑怎么找到这个可行解,进行可行性 DP,发现我们要记录四个下标,分别是两端选择的元素(最大值)及其最上面相邻两数差(或者记录次大值)。这是繁杂的,考虑到我们必然包含 \(i\) 且可行性 DP 可以把信息放在 DP 值上(只针对某些有最优决策的 DP),我们可以稍微优化。
具体来说,我们归纳出 \(3\) 种本质不同的状态(两序列本质相同):
-
\(\{(i,i-1),(b,c)\}\)
-
\(\{(i,i-2),(i-1,c)\}\)
-
\(\{(i,c),(i-1,i-2)\}\)
分类依据是 \(i-1\) 是否为 \(i\) 所在序列次大值,然后用已知量尝试填满其中一个序列,这样如果还有其他已知量可能会加在这个序列后面,然后就是已知量 \(i-2\) 分配到哪边的问题。
然后我们考虑转移,具体地,从 \(i\to i+1\),不妨令 \(i'=i+1\),分别用 \(i+1\) 塞进任意状态的任意新序列中得到:
对于状态 \([1]\):
对于状态 \([2]\):
对于状态 \([3]\):
不难发现,对于 \([2][3]\) 两个序列的最大值都是固定的,那么只需要让次大值 \(c\) 尽可能大,就能使其中一个序列的 \(\Delta=\text{序列最上方两个数之差}\) 最小化,也最有可能让转移合法化。所以对于 \([2][3]\) 我们只需要记最大 \(c\),如果不合法就记 \(-1\) 即可。
对于不同的 \(i\),这样记是 \(O(1)\) 的,滚动数组思想一下就还是 \(O(1)\) 的。
然后描述 \([1]\)。对于 \([1]\to[1]\) 的转移,发现转移条件与 \(b,c\) 无关,即如果符合条件保留上一次的所有状态即可。对于 \([1]\to [3]\) 的转移,发现把未知量移到一边就是 \(a_{i+1}\geq 2a_b -a_c\),然后转移给 \([3]\) 就是要求 \(b\) 最大化(因为 \([1]\) 的 \(b\) 对于 \([3]\) 来说是次大值),那么我们不妨用一个 STL map<int,int> 来存储下标 \(2a_b -a_c\) 的 \(b\) 的最大值,然后需要做到前缀查。类似单调栈的思想,我们保证随着限制的松弛,\(b\) 也相应地单调不减,这样每次查到最后一个 \(\le 2a_b -a_c\) 的位置就是最大 \(b\) 的位置。只需要插入的时候找到后面的下标然后如果 \(b\) 比插入的 \(b\) 还小就删除这个信息就可以了。
对于不同的 \(i\),这样记实际上是 \(O(n)\) 的,同样使用滚动数组类似的思想优化即可,加上我们要不断地清空这个 map<int,int>,所以实际占用与常数会小很多。
时间复杂度瓶颈在状态 \([1]\) 相关的转移,\(O(n\log n)\)。
代码实现中,采用了 \(f\to g\) 的方式转移,\(f\) 代表 \(i\) 的状态,\(g\) 代表 \(i+1\) 的状态。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=3e5+5;
int n,a[N];
map<int,int>f1,g1;
int main(){
int Tn;scanf("%d",&Tn);
while(Tn--){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);
f1.clear();
if(a[3]-a[2]>=a[2]-a[1])f1[a[1]]=1;
int f2=-1,f3=-1;
f2=f3=1;
for(int i=3;i<n;i++){
int g2=-1,g3=-1;
g1.clear();
int ip=i+1;
//f1 to g1
if(a[i+1]-a[i]>=a[i]-a[i-1])g1=f1;
//f1 to g3
if(!f1.empty()){
auto it=f1.upper_bound(a[i+1]);
if(it!=f1.begin()){
it=prev(it);
g3=max(g3,it->second);
}
}
if(f2!=-1&&a[i+1]-a[i]>=a[i]-a[i-2]){
//f2 to g1
auto it=g1.upper_bound(2*a[ip-2]-a[f2]);
while(it!=g1.end()){
if(it->second<=ip-2){
auto dt=next(it);
g1.erase(it);
it=dt;
}
else break;
}
g1[2*a[ip-2]-a[f2]]=max(ip-2,g1[2*a[ip-2]-a[f2]]);
}
if(f2!=-1&&a[i+1]-a[i-1]>=a[i-1]-a[f2]){
//f2 to g2
g2=max(g2,ip-3);
}
if(f3!=-1&&a[i+1]-a[i]>=a[i]-a[f3]){
//f3 to g1
auto it=g1.upper_bound(2*a[ip-2]-a[ip-3]);
while(it!=g1.end()){
if(it->second<=ip-2){
auto dt=next(it);
g1.erase(it);
it=dt;
}
else break;
}
g1[2*a[ip-2]-a[ip-3]]=max(ip-2,g1[2*a[ip-2]-a[ip-3]]);
}
if(f3!=-1&&a[i+1]-a[i-1]>=a[i-1]-a[i-2]){
//f3 to g2
g2=max(g2,f3);
}
f1=g1;f2=g2,f3=g3;
}
printf((f2!=-1||f3!=-1||!f1.empty())?"YES\n":"NO\n");
}
return 0;
}

浙公网安备 33010602011771号