【JZOJ A组】车展

3 篇文章 0 订阅

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

Data Constraint

Hint
【数据规模和约定】
对于50%的数据 n≤500,m≤1000;
对于80%的数据 n≤1000,m≤100000;
对于100%的数据n≤1000,m≤200000;
答案小于2^64。

思路

首先,要调整到的高度必为中位数,所以我们要找出这个中位数

先预处理出每个区间的答案,读入后直接输出。这时,需要维护一个大根堆和一个小根堆,其中小根堆中每个数都不小于大根堆中的最大值。枚举区间的起点,依次向后加入数,只需控制大根堆中数的个数小于等于小根堆的个数。那么大根堆中最大的数就是中位数,大根堆中所有数的和就是小于中位数的所有数的和。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=2077;
int n,m,cnt1,cnt2;
ll ans,f[maxn][maxn],a[maxn],a1[maxn],a2[maxn],sum1,sum2;
void up1(int x)
{
	int t=x;
	while(t/2>0)
	{
		if(a1[t]>a1[t>>1]) swap(a1[t],a1[t>>1]);else break;
		t>>=1;
	}
}
void down1(int x)
{
	int t=x;
	while(t*2<=cnt1)
	{
		int p=t<<1;
		if(p+1<=cnt1&&a1[p]<a1[p+1]) p++;
		if(a1[t]<a1[p]) swap(a1[t],a1[p]);else break;
		t=p;
	}
}
ll top1()
{
	return a1[1];
}
void pop1()
{
	  sum1-=a1[1]; swap(a1[cnt1],a1[1]); cnt1--; down1(1);
}
void ins1(ll x)
{
	a1[++cnt1]=x; up1(cnt1); sum1+=x;
}

void up2(int x)
{
	int t=x;
	while(t/2>0)
	{
		if(a2[t]<a2[t>>1]) swap(a2[t],a2[t>>1]);else break;
		t>>=1;
	}
}
void down2(int x)
{
	int t=x;
	while(t*2<=cnt2)
	{
		int p=t<<1;
		if(p+1<=cnt2&&a2[p]>a2[p+1]) p++;
		if(a2[t]>a2[p]) swap(a2[t],a2[p]);else break;
		t=p;
	}
}
ll top2()
{
	return a2[1];
}
void pop2()
{
	 sum2-=a2[1]; swap(a2[cnt2],a2[1]); cnt2--; down2(1);
}
void ins2(ll x)
{
	a2[++cnt2]=x; up2(cnt2); sum2+=x;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
	for(int i=1; i<=n-1; i++)
	{
		memset(a1,0,sizeof(a1)); memset(a2,0x3f,sizeof(a2));
		cnt1=cnt2=sum1=sum2=0;
		ins1(a[i]);
		for(int j=i+1; j<=n; j++)
		{
			if(cnt1==cnt2)
			{
				if(a[j]<=top2()) ins1(a[j]); else
				{
					int t=top2(); pop2(); ins1(t); ins2(a[j]);
				}
			}else
			{
				if(a[j]>=top1()) ins2(a[j]); else
				{
					int t=top1(); pop1(); ins2(t); ins1(a[j]);
				}
			}
			int t=top1();
			f[i][j]=f[j][i]=cnt1*t-sum1+sum2-cnt2*t;
		}
	}
	ans=0;
	for(int i=1; i<=m; i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ans+=f[x][y];
//		printf("s=%lld\n",f[x][y]);
	}
	printf("%lld",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值