[UOI2025] Convex Array 题解
参考了 未来姚班 zyl 的题解,在此表示对作者的感谢。
先分析一些简单的性质。题目描述中的“下凸序列”是单谷的,构造这个序列的过程相当于维护一个双端队列,先将最小的元素放进去,接下来从小到大加入每个元素(要么加入队头,要么加入队尾)。假设目前要尝试在队尾加入
尝试进行可行性 DP,将
假设当前考虑
观察到答案只可能为
想要进一步优化,需要考察上述 DP 的性质。观察到最初的 DP 记录的四个数总体上可以归类为三大种(不妨认为
后两种状态的量级运用上面的技巧可以做到
时间复杂度
放代码:
#include<bits/stdc++.h>
using namespace std;
class tree:map<int,int>{
public:
inline void set(int p,int x){
int w=(*this)[p]=max((*this)[p],x);
auto it=this->lower_bound(p);
if(it!=this->begin()&&prev(it)->second>w){
this->erase(p); return;
}
it=this->upper_bound(p);
while(it!=this->end()&&it->second<=w)it=this->erase(it);
} // 单点插入,删无用后继
inline int pre_max(int p){
auto it=this->upper_bound(p);
return it==this->begin()?-1:prev(it)->second;
} // 前缀最大值查询
inline void clear(){map<int,int>().swap(*this);}
inline bool any(){return !(this->empty());}
};
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t; cin>>t;
while(t--){
int n; cin>>n;
vector<int> a(n);
for(auto &i:a)cin>>i;
sort(a.begin(),a.end());
vector<int> f(2,-1);
tree t; t.set(a[0],0);
auto add=[&](int x,int y){t.set((a[x]<<1)-a[y],x);};
for(int i=2;i<n;i++){
vector<int> g(2,-1);
g[1]=t.pre_max(a[i]);
if(a[i]-a[i-1]<a[i-1]-a[i-2])t.clear();
if(~f[0]){
if(a[i]-a[i-1]>=a[i-1]-a[i-3])add(i-2,f[0]);
if(a[i]-a[i-2]>=a[i-2]-a[f[0]])g[0]=max(g[0],i-3);
}
if(~f[1]){
if(a[i]-a[i-1]>=a[i-1]-a[f[1]])add(i-2,i-3);
if(a[i]-a[i-2]>=a[i-2]-a[i-3])g[0]=max(g[0],f[1]);
} // 分类讨论各种转移
f=g;
}
cout<<(~f[0]||~f[1]||t.any()?"YES\n":"NO\n");
// 只要三种状态中有一种存在答案即为 YES
}
return 0;
}