题解:洛谷 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;
}

浙公网安备 33010602011771号