1 条题解
-
0
少人做的好题!
这里提供主席树的做法。题意概况
给定一个序列 ,
每次都选出三元组 满足 且 。
然后,将 增加 ,所需代价为 。
求:在若干次操作之后,让序列 满足 $h_1 \leq h_2 \leq \ldots \leq h_p \geq \ldots \geq h_N$ 的最小代价。思路
我们设 是满足当前 的最大值, 为满足 的最小值。
我们很容易发现,每次都是选择区间 的最小值来更新。(下面的加粗的“最小值”都是这个意思)单个最小值简单。
但此时如果有多个最小值,该以怎样的顺序来更新呢?举个例子:
红色柱子即此时需要修改的最小值
有一种最优的操作方案:先更新区间 中最左和最右的最小值,再更新中间的最小值。
为什么
我们每次都是选出最小值来更新,先更新最左和最有右的最小值,再跟新中间的,左边大于最小值的最小值和右边大于最小值的最小值都变为了 ,那么一次更新的最小代价就为 ,能保证这是最优的方案(下面有 的定义)。我们不妨设这个最小值为 ,有 个最小值,最左边的最小值编号为 ,最右边为 。
我们称二元组 为 的最好二元组,当且仅当满足 且 是满足 的最小值, 同理,也是满足 的最小值。
我们先找到 和 的最好二元组 和 。
我们考虑先修改 的值,再修改 的值,最后修改中间的最小值,其代价为
$$(h_{q_L} + h_{p_L} + x) + (x + 1 + h_{p_R} + x) + (k - 2) \times (x + 1 + x + 1 + x)$$化简为
我们还可以先修改 的值。
$$h_{q_L} + \min(h_{p_L}, h_{q_R}) + h_{p_R} + 2k - 3 + (3k - 3)x$$
所以将所有最小值增加 的最小代价为我们发现公式中 查询的范围包括了整个序列 ,那么返回的值一定是大于 的最小值,即次小值。
设 为当前的次小值, 那么将所有最小值增加到次小值的最小代价为
$$(h_{q_L} + h_{p_R} + y + 2k - 3)(y - x) + (3k - 3) \times \frac{(x + y - 1)(y - x)}{2}$$优化
我们再离散化一下,就可以做到 了。
至于最小值和 ,,可以用 set 来维护,求区间大于某值的最小值是主席树的模板题。诶?为什么不用修改值呢?
我们每次查询都只查找比最小值大的数,所以改不改值都没影响,维护 和 set 时只要将小于 的数当成 就行了。
诶?公式里有个除 ,有没有不用逆元就可以处理的方法?
有的有的,将模数 开成 ,再在最后结果模回 就行了。 ~( •̀ ω •́ )耶,又少写几行代码~
故最终时间复杂度就为 。
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e6+100,oo=0x3f3f3f3f,mod=2e6+6; ll n,ans,top,tot; int h[N]; ll f[N]; struct node{ int l,r,s; }; node t[N*60]; int rt[N]; vector<int> va[N]; set<int> st; void lsh(int a[]){ map<int,int> mp; for(int i=1;i<=n;i++) mp[a[i]]; for(auto &it:mp) it.second=++tot,f[tot]=it.first%mod; for(int i=1;i<=n;i++) a[i]=mp[a[i]]; } inline int clone(int v){ t[++top]=t[v]; return top; } int build(int v,int tl,int tr){ v=++top; if(tl==tr) t[v].s=0; else{ int tm=(tl+tr)>>1,&L=t[v].l,&R=t[v].r; L=build(L,tl,tm),R=build(R,tm+1,tr); t[v].s=t[L].s+t[R].s; } return v; } inline int update(int v,int tl,int tr,int x){ v=clone(v); if(tl==tr) t[v].s++; else{ int tm=(tl+tr)>>1,&L=t[v].l,&R=t[v].r; if(x<=tm) L=update(L,tl,tm,x); else R=update(R,tm+1,tr,x); t[v].s=t[L].s+t[R].s; } return v; } inline int ask(int u,int v,int tl,int tr,int k){ if(tl==tr) return tl>=k?tl:oo; int tm=(tl+tr)>>1; int lcnt=t[t[v].l].s-t[t[u].l].s; if(lcnt&&k<=tm){ int res=ask(t[u].l,t[v].l,tl,tm,k); if(res!=oo) return res; } int rcnt=t[t[v].r].s-t[t[u].r].s; if(!rcnt) return oo; return ask(t[u].r,t[v].r,tm+1,tr,k); } inline void add(int x,const int& y,const int& l,const int& r){ while(x<y){ x++; for(int v:va[x]) if(v>=l&&v<=r) st.insert(v); } } inline void cheak(const int &l,const int &r){ while(!st.empty()&&*st.begin()<l) st.erase(st.begin()); while(!st.empty()&&*st.rbegin()>r) st.erase(prev(st.end())); } int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n; for(int i=1;i<=n;i++) cin>>h[i]; lsh(h); rt[0]=build(rt[0],1,tot); for(int i=1;i<=n;i++) va[h[i]].push_back(i); for(int i=1;i<=n;i++) rt[i]=update(rt[i-1],1,tot,h[i]); int l=1,r=n,x=0; while(1){ while(l<r&&max(x,h[l+1])>=h[l]) l++; while(l<r&&max(x,h[r-1])>=h[r]) r--; if(!(l+1<r)) break; cheak(l,r); int k=st.size(); if(k==0){ add(x,x+1,l,r),x++; continue; } int L=*st.begin(),R=*st.rbegin(); int qL=ask(rt[0],rt[L],1,tot,x+1); int pR=ask(rt[R-1],rt[n],1,tot,x+1); int V=x+1; ll num=(f[V]-f[x]+mod)%mod; ll H=(f[x]+f[V]-1)*num/2%mod; if(k==1) ans=(ans+(f[qL]+f[pR])*num+H)%mod; else ans=(ans+(f[qL]+f[pR]+f[V]+2*k-3+mod)*num+H*(3*k-3))%mod; add(x,V,l,r),x=V; } cout<<ans%int(1e6+3); return 0; }
- 1
信息
- ID
- 7325
- 时间
- 5000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者