车展
Time Limits: 1000 ms Memory Limits: 65536 KB
Description
遥控车是在是太漂亮了,韵韵的好朋友都想来参观,所以游乐园决定举办m次车展。车库里共有n辆车,从左到右依次编号为1,2,…,n,每辆车都有一个展台。刚开始每个展台都有一个唯一的高度h[i]。主管已经列好一张单子:
L1 R1
L2 R2
…
Lm Rm
单子上的(Li,Ri)表示第i次车展将要展出编号从Li到Ri的车。
为了更加美观,展览时需要调整展台的高度,使参展所有展台的高度相等。展台的高度增加或减少1都需花费1秒时间。由于管理员只有一个人,所以只好对每个展台依次操作。每次展览结束后,展台高度自动恢复到初始高度。
请告诉管理员为了举办所有展览,他最少需要花多少时间将展台调整好。
Input
第一行为两个正整数n、m。
第二行共n个非负整数,表示第i辆车展台的高度h[i]。
接下来m行每行2个整数Li、Ri(Li≤Ri)。
Output
一个正整数,调整展台总用时的最小值。
Sample Input
6 4
4 1 2 13 0 9
1 5
2 6
3 4
2 2
Sample Output
48
Hint
【数据规模和约定】
对于50%的数据 n≤500,m≤1000;
对于80%的数据 n≤1000,m≤100000;
对于100%的数据n≤1000,m≤200000;
答案小于2^64。
题目大意
给出n个高低不同的展台,它可以升降,升一个单位或降一个单位需要1个花费,有给出你m个询问,第i个询问有一个范围[Ai,Bi],现在让你将区间[Ai,Bi]内的每个站台从原来的状况,升降到同一高度,得到一个单次询问花费Vi,求所有Vi的和的最小值。
解题思路
因为要得到最小花费,那么变化后的目的高度一定是这个区间的中位数。
我们可以先预处理答案,求出每个区间的最小花费总和,在这里,我们可以维护两个堆,第一个堆是小根堆,即小顶堆,存这个区间中比较大的那一半;另一个是大顶堆,存这个区间较小的一半,每次保证小顶堆当前的容量小于等于大顶堆的容量,且容量最多相差1,那么这个区间的中位数就是大顶堆的堆顶。
但我们如果再去一个个升或降的话,就需要O(n)一共O(n^3) <<Boom!!!>> <script type="math/tex" id="MathJax-Element-15">< ></script>,于是,我们观察一下计算结果的式子:
假如我们将A小到大排序一下,变成B,那么式子就变为
(x−B1)+(x−B2)+...+(x−Bmid)+(Bmid+1−x)+...+(Bn−1−x)+(Bn−x)
转换一下:
发现
当n是偶数时, (∑[n2]i=1Bi) 是小根堆的总和, (∑nj=[n2+1]Bj) 是大根堆的总和
当n是奇数时, (∑[n2]i=1Bi) 是小根堆的总和, (∑nj=[n2+1]Bj) 是大根堆的总和减去中位数
于是乎我们就可以在插入堆的同时维护两个值分别表示较大那个堆(小顶堆)的和,和较小那个堆(大顶堆)的和。
Codes:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
int n,m;
long long tot=0,a[1001],maxx[1001],minn[1001],ans[1001][1001],amax,amin;
int getmax();//从大顶堆中取出
int getmin();//从小顶堆中取出
void putmax(int);//加入大顶堆
void putmin(int);//加入小顶堆
void upmax(int);
void upmin(int);
void downmax(int);
void downmin(int);
int main()
{
scanf("%d%d",&n,&m);
fo(i,1,n)scanf("%lld",&a[i]);
fo(i,1,n-1)
{
memset(minn,0x7f,sizeof(minn));
amin=minn[0]=maxx[0]=0;//初始化堆
maxx[++maxx[0]]=a[i];/*amax和amin为各堆内元素和*/
amax=a[i];
fo(j,i+1,n)
{
if(maxx[0]==minn[0])//两堆容量相等,在最后使得大顶堆多一
{
if(a[j]<minn[1])/*元素要放入大顶堆*/
{
putmax(a[j]);
amax+=a[j];
}else{/*元素要放到小顶堆的时候,把小顶堆的堆顶移至大顶堆,存入元素*/
int x=getmin();
putmin(a[j]);
putmax(x);
amax+=x;
amin+=a[j]-x;
}
}else{/*两堆容量不等,在最后使得相等*/
if(maxx[1]>a[j])
{
int x=getmax();
putmax(a[j]);
putmin(x);
amin+=x;
amax+=a[j]-x;
}else{
putmin(a[j]);
amin+=a[j];
}
}
if((j-i)%2==0)
ans[i][j]=ans[j][i]=amin-amax+maxx[1];
else
ans[i][j]=ans[j][i]=amin-amax;/*求答案*/
}
}
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
tot+=ans[x][y];
}
printf("%lld",tot);
}
int getmax()
{
int ans=maxx[1];
maxx[1]=maxx[maxx[0]--];
downmax(1);
return ans;
}
int getmin()
{
int ans=minn[1];
minn[1]=minn[minn[0]--];
downmin(1);
return ans;
}
void putmax(int x)
{
maxx[++maxx[0]]=x;
upmax(maxx[0]);
}
void putmin(int x)
{
minn[++minn[0]]=x;
upmin(minn[0]);
}
void upmin(int x)
{
if(x==1)return;
if(minn[x]>=minn[x>>1])return;
swap(minn[x>>1],minn[x]);
upmin(x>>1);
}
void upmax(int x)
{
if(x==1)return;
if(maxx[x]<=maxx[x>>1])return;
swap(maxx[x>>1],maxx[x]);
upmax(x>>1);
}
void downmax(int x)
{
if(x*2>maxx[0])return;
int a,b=-1;
a=maxx[x*2];
if(x*2+1<=maxx[0])b=maxx[x*2+1];
if(maxx[x]<a)
{
if(a<b)
{
swap(maxx[x],maxx[x*2+1]);
downmax(x*2+1);
}else{
swap(maxx[x],maxx[x*2]);
downmax(x*2);
}
}else if(maxx[x]<b)
{
swap(maxx[x],maxx[x*2+1]);
downmax(x*2+1);
}
}
void downmin(int x)
{
if(x*2>minn[0])return;
int a,b=0x7fffffff;
a=minn[x*2];
if(x*2+1<=minn[0])b=minn[x*2+1];
if(minn[x]>a)
{
if(a>b)
{
swap(minn[x],minn[x*2+1]);
downmin(x*2+1);
}else{
swap(minn[x],minn[x*2]);
downmin(x*2);
}
}else if(minn[x]>b)
{
swap(minn[x],minn[x*2+1]);
downmin(x*2+1);
}
}