BZOJ1592 POJ3666 [Usaco2008 Feb]Making the Grade 路面修整 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - POJ3666
题目传送门 - BZOJ1592
题意概括
整条路被分成了N段,N个整数A_1, ... , A_N (1 <= N <= 2,000)依次描述了每一段路的高度(0 <= A_i <= 1,000,000,000)。FJ希望找到一个恰好含N个元素的不上升或不下降序列B_1, ... , B_N,作为修过的路中每个路段的高度。由于将每一段路垫高或挖低一个单位的花费相同,修路的总支出可以表示为: |A_1 - B_1| + |A_2 - B_2| + ... + |A_N - B_N| 请你计算一下,FJ在这项工程上的最小支出是多少。FJ向你保证,这个支出不会超过2^31-1。
题解
和这个差不多。
只需要做一次之后把原序列翻转再做一次就可以了。
不用-i,因为这次是非递增或者非递减。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int N=1000005; int n,v[N],root[N],L[N],R[N],top; int ls[N],rs[N],npl[N],size[N],val[N]; void make_heap(int x,int v){ ls[x]=rs[x]=npl[x]=0,val[x]=v,size[x]=1; } int merge(int a,int b){ if (1LL*a*b==0) return a+b; if (val[a]<val[b]) swap(a,b); rs[a]=merge(rs[a],b); if (npl[rs[a]]>npl[ls[a]]) swap(rs[a],ls[a]); npl[a]=npl[rs[a]]+1; size[a]=size[ls[a]]+size[rs[a]]+1; return a; } void pop(int &x){ x=merge(ls[x],rs[x]); } int main(){ scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&v[i]); LL ans=1LL<<50; for (int cc=0;cc<2;cc++){ top=0; for (int i=1;i<=n;i++){ make_heap(root[++top]=i,v[i]); L[top]=R[top]=i; while (top>1&&val[root[top-1]]>val[root[top]]){ top--; root[top]=merge(root[top],root[top+1]); R[top]=R[top+1]; while (size[root[top]]*2>R[top]-L[top]+2) pop(root[top]); } } LL ans1=0; for (int i=1;i<=top;i++) for (int j=L[i];j<=R[i];j++) ans1+=abs(val[root[i]]-v[j]); ans=min(ans,ans1); for (int i=1;i<=n/2;i++) swap(v[i],v[n+1-i]); } printf("%lld",ans); return 0; }