activeO
照彻万川

导航

 

题源

发现 \(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;
}
posted on 2024-08-13 20:36  activeO  阅读(2)  评论(0编辑  收藏  举报