正题
题目链接:https://jzoj.net/senior/#main/show/3859
题目大意
nnn个台阶,第iii个高度为hih_ihi,把它分为两个集合,使得两个集合中相邻的hih_ihi差值和最小。
解题思路
设fif_ifi表示刚好处理到iii且目前集合开头是iii的最小差值
定义si=∑j=1i∣hj−hj−1∣s_i=\sum_{j=1}^i|h_j-h_{j-1}|si=j=1∑i∣hj−hj−1∣
然后有转移方程fi=min{fj+si−1−sj+∣hi−hj−1∣}(j∈[1..i−1])f_{i}=min\{f_j+s_{i-1}-s_j+|h_i-h_{j-1}|\}(j\in[1..i-1])fi=min{fj+si−1−sj+∣hi−hj−1∣}(j∈[1..i−1])
考虑用数据结构优化上面的dpdpdp,我们将情况分为:
- hi≥hj−1:h_i\geq h_{j-1}:hi≥hj−1:有转移fi=si−1+hi+min{fj−sj−hj−1}f_i=s_{i-1}+h_i+min\{f_j-s_{j}-h_{j-1}\}fi=si−1+hi+min{fj−sj−hj−1}
- hi≤hj−1:h_i\leq h_{j-1}:hi≤hj−1:有转移fi=si−1−hi+min{fj−sj+hj−1}f_i=s_{i-1}-h_i+min\{f_j-s_{j}+h_{j-1}\}fi=si−1−hi+min{fj−sj+hj−1}
用两个树状数组分开维护后面的minminmin值即可。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define lowbit(x) (x&-x)
using namespace std;
const ll N=5e5+10;
ll n,m,h[N],b[N],s[N],f[N],ans;
struct Tree_Array{ll t[N];void Change(ll x,ll val){while(x<=m){t[x]=min(t[x],val);x+=lowbit(x);}}ll Ask(ll x){ll ans=2147483647;while(x){ans=min(ans,t[x]);x-=lowbit(x);}return ans;}
}Ta,Tp;
int main()
{//freopen("data.txt","r",stdin);memset(Ta.t,0x3f,sizeof(Ta.t));memset(Tp.t,0x3f,sizeof(Tp.t));scanf("%lld",&n);for(ll i=1;i<=n;i++){scanf("%lld",&h[i]);b[i]=h[i];s[i]=s[i-1]+abs(h[i]-h[i-1]);}b[n+1]=0;sort(b+1,b+2+n);m=unique(b+1,b+2+n)-b-1;ans=1e18;ll lp=1,la=m;for(ll i=1;i<=n;i++){ll pre=lower_bound(b+1,b+1+m,h[i])-b;ll aft=m-pre+1;f[i]=min(s[i-1]+h[i]+Tp.Ask(pre),s[i-1]-h[i]+Ta.Ask(aft));if(i==1) f[i]=h[i];Tp.Change(lp,f[i]-s[i]-h[i-1]);Ta.Change(la,f[i]-s[i]+h[i-1]);ans=min(ans,f[i]+s[n]-s[i]);la=aft;lp=pre;}printf("%lld",ans);
}