题解:洛谷 P10067 ([CCO 2023] Real Mountains)

1. Description

给定一个长度为 \(n\) 的序列 \(\{h\}\),你可以通过三元组 \((i,j,k)\ (i<j<k,h_i>h_j,h_k>h_j)\) 来使 \(h_i\) 增加一,代价为 \(h_i+h_j+h_k\),要求使得修改后的序列 \(h\) 存在 \(p\),使得 \(h_1\le h_2\le \dots \le h_p\)\(h_p\ge h_{p-1}\ge \dots \ge h_1\)

2. Solution

首先一个显然的结论是,满足要求的序列一定是 \(a_i=\min(\max_{j=1}^{i} h_j,\max_{j=i}^{n} h_j)\),当你将序列修改为 \(a\) 的时候,你就不能继续操作了,因为此时 \(a\) 一定是前缀最大值或者后缀最大值。
那么现在我们的目标就是要求出到达这个序列的最小花费。
显然如果在某时刻 \(h_i<h_j\),且二者均需要操作,我们一定会优先操作 \(i\),原因显然,这里不过多解释。
但是如果在某时刻有多个高度相同且都需要修改的位置呢?我们发现,一定是先操作左右,后操作中间的,这样可以使得花费最小。
那么现在就很简单了,我们维护一个集合 \(st\) 表示当前需要修改的位置集合,然后取出最左边的位置 \(be\),最右边的位置 \(en\),分别求出 \(be\) 两边大于它的最小值和 \(en\) 两边大于它的最小值,求出最小代价加入答案即可。
怎么计算前后缀中大于某一个数的最小值?因为我们是依次增加高度的,所以前缀,后缀中被修改过的位置一定不高于现在需要修改的位置的高度,对答案没有影响,所以我们可以直接用两棵主席树维护。
那么现在就可以得到一个 \(O(V\log n)\) 的算法。
发现,对于同样的集合 \(st\),随着当前的高度的增加,代价是等差数列,直接等差数列求和即可,可以使用离散化来加快这一过程,将时间复杂度优化至 \(O(n\log n)\)

3. Code

/*by ChenMuJiu*/
/*略去缺省源与快读快写*/
const ll N=1e6+5,inf=0x3f3f3f3f,mod=1e6+3;
ll n,ans;
ll h[N],a[N];
int tot;
int tmp[N],val[N],pre[N],suf[N];
set<int>st;
vector<int>pos[N],del[N];
ll cal(ll l,ll r,ll cnt){
	if((r+l)&1)return ((cnt>>1)%mod)*((l+r)%mod)%mod;
	return ((l+r>>1)%mod)*(cnt%mod)%mod;
}
struct Segment_tree{
	int num;
	int c[N*25],ls[N*25],rs[N*25];
	#define mid (l+r>>1)
	int New(){
		int p=++num;
		c[p]=0,ls[p]=0,rs[p]=0;
		return p;
	}
	int copy(int p){
		int q=New();
		c[q]=c[p],ls[q]=ls[p],rs[q]=rs[p];
		return q;
	}
	void pushup(int p){
		c[p]=c[ls[p]]+c[rs[p]];
	}
	int build(int l,int r){
		ll p=New();
		if(l==r)return p;
		ls[p]=build(l,mid),rs[p]=build(mid+1,r);
		return p;
	}
	int change(int p,int l,int r,int x){
		int q=copy(p);
		if(l==r){
			c[q]++;
			return q;
		}
		if(mid>=x)ls[q]=change(ls[p],l,mid,x);
		else rs[q]=change(rs[p],mid+1,r,x);
		pushup(q);
		return q;
	}
	int query(int p,int l,int r,int x){
		if(l==r){
			if(l>=x&&c[p])return tmp[l];
			return -1;
		}
		if(l>=x){
			if(c[ls[p]])return query(ls[p],l,mid,x);
			if(c[rs[p]])return query(rs[p],mid+1,r,x);
			return -1;
		}
		int tmp=-1;
		if(mid>=x)tmp=query(ls[p],l,mid,x);
		if(~tmp)return tmp;
		return query(rs[p],mid+1,r,x);
	}
	#undef mid
}Setpre,Setsuf; 
signed main(){
	read(n);
	for(int i=1;i<=n;i++)
		read(h[i]),a[i]=inf;
	for(ll i=1,pre=0;i<=n;i++){
		tomax(pre,h[i]);
		tomin(a[i],pre);
	}
	for(ll i=n,suf=0;i>=1;i--){
		tomax(suf,h[i]);
		tomin(a[i],suf);
	}
	for(int i=1;i<=n;i++)
		tmp[i]=h[i];
	sort(tmp+1,tmp+n+1);
	tot=unique(tmp+1,tmp+n+1)-tmp-1;
	for(int i=1;i<=n;i++)
		val[i]=lower_bound(tmp+1,tmp+tot+1,h[i])-tmp;
	
	pre[0]=Setpre.build(1,tot);
	for(ll i=1;i<=n;i++)
		pre[i]=Setpre.change(pre[i-1],1,tot,val[i]);
	suf[n+1]=Setsuf.build(1,tot);
	for(ll i=n;i>=1;i--)
		suf[i]=Setsuf.change(suf[i+1],1,tot,val[i]);
		
	ll mi=inf;
	for(int i=1;i<=n;i++){
		if(a[i]!=h[i]){
			ll tar=lower_bound(tmp+1,tmp+tot+1,a[i])-tmp;
			tomin(mi,val[i]);
			pos[val[i]].push_back(i);
			del[tar].push_back(i);
		}
	}
	if(mi==inf){
		puts("0");
		exit(0);
	}
	for(int now=1;now<tot;now++){
		for(auto x:del[now])
			st.erase(x);
		for(auto x:pos[now])
			st.insert(x);
		if(st.empty())continue;
		if(st.size()==1){
			int x=*st.begin();
			ll L=Setpre.query(pre[x-1],1,tot,now+1);
			ll R=Setsuf.query(suf[x+1],1,tot,now+1);
			(ans+=cal(L+R+tmp[now],L+R+tmp[now+1]-1,tmp[now+1]-tmp[now]))%=mod;
		}else{
			int be=*st.begin(),en=*st.rbegin();
			ll Lbe=Setpre.query(pre[be-1],1,tot,now+1);
			ll Rbe=Setsuf.query(suf[be+1],1,tot,now+1);
			ll Len=Setpre.query(pre[en-1],1,tot,now+1);
			ll Ren=Setsuf.query(suf[en+1],1,tot,now+1);
			ll l=min(Lbe+Rbe+Ren,Len+Ren+Lbe)+3*tmp[now]+1+(3*tmp[now]+2)*(st.size()-2);
			ll r=min(Lbe+Rbe+Ren,Len+Ren+Lbe)+3*(tmp[now+1]-1)+1+(3*(tmp[now+1]-1)+2)*(st.size()-2);
			(ans+=cal(l,r,tmp[now+1]-tmp[now]))%=mod;
		}
	}
	write(ans),Nxt;
}
posted @ 2026-02-03 20:44  陈牧九  阅读(2)  评论(0)    收藏  举报