发现 \(v\) 范围很小,有一个基于 \(v\) 的策略就是从 \(1\) 开始往上能合并就合并,这样一定不劣。
于是考虑将序列划分为若干个值相等的段,形如 \((num_{x},x)\),对于一个区间的段,如果有一段比两边相邻的段的数都要小,此时这个段的长度显然不会增加,所以可以直接合并,推平成两边小的那个段的数。
一个段在合并时,如果是 \(2^k\) 可以直接合并 \(k\) 次,不然一定会有剩下来一些没用的,又因为合并时这个数一定不会再增加,所以他会阻止两边合并,于是两边都下取整分成左右两边考虑。(中间加一个分隔符)
用线段树维护一个区间的段的状态,如果区间中有两个分隔符,分隔符中的段就已经可以直接计算了,然后把整一段看成一个分隔符,实现上可以取分隔符为 \((1,inf)\),这样他一定会进行合并。
所以我们线段树中维护的一定是一个至多一个分隔符的,类似上凸的东西,所以每个区间只会维护 \(O(v)\) 个段,两个区间信息合并就是把右区间的每一段插入左区间,复杂度可以接受。
查询区间时把剩下这些段合并即可。
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
struct Tree{
vector<pii> vec;
int val;
Tree(){}
Tree(vector<pii> vv,int ll){
vec=vv;
val=ll;
}
}tr[maxn<<2];
int a[maxn],lg[maxn];
inline void init(int n){
lg[0]=-1;
for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
}
void ins(Tree &now,pii x){
if(now.vec.empty()){
now.vec.push_back(x);
return;
}
pii tmp=now.vec.back();
int lt=(int)now.vec.size();
if(tmp.first==x.first) now.vec.back().second+=x.second;
else if(lt>=2&&now.vec[lt-2].first>tmp.first&&tmp.first<x.first){
now.val=max(now.val,tmp.first+lg[tmp.second]);
now.vec.pop_back();
int tt=min(now.vec[lt-2].first,x.first)-tmp.first;
if(tmp.second<(1<<tt)) ins(now,make_pair(inf,1));
else if(((tmp.second>>tt)<<tt)==tmp.second) ins(now,make_pair(tmp.first+tt,tmp.second>>tt));
else{
ins(now,make_pair(tmp.first+tt,tmp.second>>tt));
ins(now,make_pair(inf,1));
ins(now,make_pair(tmp.first+tt,tmp.second>>tt));
}
ins(now,x);
}else now.vec.push_back(x);
}
Tree merge(Tree u,Tree v){
Tree res=u;
res.val=max(res.val,v.val);
for(pii x : v.vec) ins(res,x);
return res;
}
void pushup(int now){
tr[now]=merge(tr[now<<1],tr[now<<1|1]);
}
void build(int now,int l,int r){
if(l==r){
tr[now].vec.clear();
tr[now].val=a[l];
tr[now].vec.push_back(make_pair(a[l],1));
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
pushup(now);
}
void update(int now,int l,int r,int x,int v){
if(l==r){
tr[now].vec.clear();
tr[now].val=v;
tr[now].vec.push_back(make_pair(v,1));
return;
}
int mid=(l+r)>>1;
if(x<=mid) update(now<<1,l,mid,x,v);
else update(now<<1|1,mid+1,r,x,v);
pushup(now);
}
Tree query(int now,int l,int r,int ql,int qr){
if(ql<=l&&qr>=r) return tr[now];
int mid=(l+r)>>1;
if(qr<=mid) return query(now<<1,l,mid,ql,qr);
else if(ql>mid) return query(now<<1|1,mid+1,r,ql,qr);
else return merge(query(now<<1,l,mid,ql,qr),query(now<<1|1,mid+1,r,ql,qr));
}
Tree ret;
int main(){
freopen("remove.in","r",stdin);
freopen("remove.out","w",stdout);
int n,q;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&q);
init(n);
build(1,1,n);
while(q--){
int op,x,y;
scanf("%d %d %d",&op,&x,&y);
if(op==2) update(1,1,n,x,y);
else{
Tree tmp=query(1,1,n,x,y);
ret.vec.clear();
ret.val=tmp.val;
ins(ret,make_pair(inf,1));
for(pii x : tmp.vec) ins(ret,x);
ins(ret,make_pair(inf,1));
printf("%d\n",ret.val);
}
}
return 0;
}