专栏文章
题解:P14311 【MX-S8-T4】平衡三元组
P14311题解参与者 11已保存评论 11
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 11 条
- 当前快照
- 1 份
- 快照标识符
- @minhy2q6
- 此快照首次捕获于
- 2025/12/02 02:42 3 个月前
- 此快照最后确认于
- 2025/12/02 02:42 3 个月前
我们从暴力开始考虑,对于一个确定的区间 和 , 和 一定会取到 和 的前/后缀最大值。
感性理解一下, 确定时, 和 越大,更容易满足 的限制,同时会使价值更大,是一个双赢的局面。
处理一下区间前/后缀最大值,枚举 ,即可做到 的暴力做法。
进一步思考,当答案最优时, 或 一定会取到区间内的最大值(除非 不得不取到最大值),所以我们就可以确定三元组的一个端点。
我们先考虑 取到最大值的情况,设 ,我们求出 的最大值的位置为 ,那么所有在 之间的 都是合法的,因为限制式子相当于让 ,现在 和 都大于等于 ,所以他们的平均值也一定大于等于 , 所以我们直接取 中的 的最大值计算贡献即可。
接下来我们 的情况, 已经确定, 沿用暴力时的思路,取 中的最大值 作为 判断是否合法。
如果合法,该答案即为最优答案,因为 分别是可取区间内的最大值的位置,不会再有合法区间内的其他值使答案更优。
如果不合法,我们继续向后重复这个过程,选定 为 区间内的最大值的位置,对这个子问题递归求解即可,直到满足要求或区间长度不足选出两个数。
的情况同理。
最后我们证明时间复杂度:
主要的复杂度分析在于递归的次数的数量级,我们考虑何时会进行下一层递归:
移项得
子问题的递归条件同理为
归纳总结可得递归条件为
显然最多递归 次,其中 为值域。
我们在这个过程中只用到了求区间最大值及其位置,可以用线段树平凡的维护,修改操作的区间加是平凡的。
所以复杂度为
这道题的代码是比较好写的,是一道偏思维的 DS 题。
C#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,a[(int)2e6];
struct Seg{
#define ls i<<1
#define rs i<<1|1
#define mid ((l+r)>>1)
int mx[(int)5e6],pos[(int)5e6],laz[(int)5e6];
void build(int i,int l,int r){
if(l==r)return mx[i]=a[l],pos[i]=l,void();
build(ls,l,mid),build(rs,mid+1,r),push_up(i);
}
void down(int i){
if(!laz[i])return ;
mx[ls]+=laz[i],mx[rs]+=laz[i];
laz[ls]+=laz[i],laz[rs]+=laz[i];laz[i]=0;
}
void push_up(int i){
mx[i]=max(mx[ls],mx[rs]);
pos[i]=mx[i]==mx[ls]?pos[ls]:pos[rs];
}
void add(int i,int l,int r,int x,int y,int k){
if(x<=l&&y>=r)return mx[i]+=k,laz[i]+=k,void();
down(i);if(x<=mid)add(ls,l,mid,x,y,k);
if(y>mid)add(rs,mid+1,r,x,y,k);push_up(i);
}
pair<int,int> query(int i,int l,int r,int x,int y){
if(x<=l&&y>=r)return {mx[i],pos[i]};pair<int,int> ans={-1e9,0};
down(i);if(y<=mid)return query(ls,l,mid,x,y);
if(x>mid)return query(rs,mid+1,r,x,y);
auto [val1,P1]=query(ls,l,mid,x,y);
auto [val2,P2]=query(rs,mid+1,r,x,y);
return {max(val1,val2),val1>=val2?P1:P2};
}int operator [](const int &x){return this->query(1,1,n,x,x).first;}
int operator [](const pair<int,int> x){return this->query(1,1,n,x.first,x.second).first;}
#undef mid
}T;
int ans=0;
void solver(int l,int r,int Mx){
// cout<<l<<' '<<r<<'\n';
if(l>=r)return ;
auto [mx,pos]=T.query(1,1,n,l,r);
if(pos!=l)ans=max(ans,T[{l,pos-1}]+mx+Mx);
if(pos==r)return ;
if(2*mx<=Mx+T[{pos+1,r}])return ans=max(ans,mx+T[{pos+1,r}]+Mx),void();
else solver(pos+1,r,Mx);
}
void solvel(int l,int r,int Mx){
// cout<<l<<' '<<r<<'\n';
if(l>=r)return ;
auto [mx,pos]=T.query(1,1,n,l,r);
if(pos!=r)ans=max(ans,T[{pos+1,r}]+mx+Mx);
if(pos==l)return ;
if(2*mx<=Mx+T[{l,pos-1}])return ans=max(ans,mx+T[{l,pos-1}]+Mx),void();
else solvel(l,pos-1,Mx);
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
// freopen("D.in","r",stdin),freopen("D.out","w",stdout);
cin>>n>>q;
for(int i=1;i<=n;i++)cin>>a[i];
T.build(1,1,n);
for(int i=1,opt,x,y,k;i<=q;i++){
cin>>opt>>x>>y;
if(opt==1){
ans=-1e9;
auto [Mx,Pos]=T.query(1,1,n,x,y);
solvel(x,Pos-1,Mx),solver(Pos+1,y,Mx);
if(ans==-1e9)cout<<"No\n";
else cout<<ans<<'\n';
}else cin>>k,T.add(1,1,n,x,y,k);
}
}
相关推荐
评论
共 11 条评论,欢迎与作者交流。
正在加载评论...