您的位置:首页 > 其它

车展

2016-07-08 22:41 253 查看

车展

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!!!>><>,于是,我们观察一下计算结果的式子:

∑i=1n|Ai−x|\sum_{i=1}^n |A_i - x|(x为中位数)

假如我们将A小到大排序一下,变成B,那么式子就变为

(x−B1)+(x−B2)+...+(x−Bmid)+(Bmid+1−x)+...+(Bn−1−x)+(Bn−x)(x-B_1)+(x-B_2)+...+(x-B_{mid})+(B_{mid+1}-x)+...+(B_{n-1}-x)+(B_n-x)

转换一下:(∑j=[n2+1]nBj)−(∑i=1[n2]Bi)(\sum_{j=[\frac{n}{2}+1]}^n B_j) - (\sum_{i=1}^{[\frac{n}{2}]} B_i)

发现

当n是偶数时,(∑[n2]i=1Bi)(\sum_{i=1}^{[\frac{n}{2}]} B_i)是小根堆的总和,(∑nj=[n2+1]Bj)(\sum_{j=[\frac{n}{2}+1]}^n B_j)是大根堆的总和

当n是奇数时,(∑[n2]i=1Bi)(\sum_{i=1}^{[\frac{n}{2}]} B_i)是小根堆的总和,(∑nj=[n2+1]Bj)(\sum_{j=[\frac{n}{2}+1]}^n B_j)是大根堆的总和减去中位数

于是乎我们就可以在插入堆的同时维护两个值分别表示较大那个堆(小顶堆)的和,和较小那个堆(大顶堆)的和。

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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: