BZOJ1592 POJ3666 [Usaco2008 Feb]Making the Grade 路面修整 左偏树 可并堆 欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - POJ3666 题目传送门 - BZOJ1592 题意概括 题解 代码

去博客园看该题解


题目传送门 - 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;
}