您的位置:首页 > 其它

【Vijos1459】车展

2015-08-07 09:46 417 查看

【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


【题解】

  看到题目的时候以为求平均数结果看到样例就不对。很简单的道理,给你一个数据: 

1 1000 1000 1000


  将四个数都换成750(平均数750.25)明显没有将1换成1000的时间短。然后我们就要考虑这个数是哪一个。参考这里Vijos1459 车展,我们可以知道就是要求中位数。接下来就是实现的问题了,上方博文中给出了一种巧妙的O(n2)的算法。而我最近练习Treap正好就用这个写一种时间复杂度为O(n2log2n)的算法,根据数据范围:

对于50%的数据 n≤500,m≤1000;

对于80%的数据 n≤1000,m≤100000;

对于100%的数据n≤1000,m≤200000;

答案在2^64以内。

可以知道在规定时间内可以运行完。

  

  参考代码如下:

type treap = record
rnd,size,w,val:longint;
//rnd记录优先,size记录子树大小,w记录和本节点数值相同的点的个数,val记录节点的值
l,r:longint;  //l和r分别是左儿子和右儿子编号
sum:int64;    //以此节点为根的子树中所有值的总和
end;
var n,m,root,cnt:longint;
tot,anstot,tmp,num:int64;
h:array[0..1010] of longint;
ans:array[0..1010,0..1010] of longint;
tree:array[0..1010] of treap;
procedure init;  //读入
var i:longint;

4000
begin
readln(n,m);
for i:=1 to n do read(h[i]); readln;
end;
procedure update(k:longint);  //维护节点
begin
tree[k].size:=tree[tree[k].l].size+tree[tree[k].r].size+tree[k].w;
tree[k].sum:=tree[tree[k].l].sum+tree[tree[k].r].sum+tree[k].val*tree[k].w;
end;
procedure right_rotation(var k:longint);  //右旋
var tmp:longint;
begin
tmp:=tree[k].l; tree[k].l:=tree[tmp].r; tree[tmp].r:=k;
update(k); update(tmp); k:=tmp;
end;
procedure left_rotation(var k:longint);  //左旋
var tmp:longint;
begin
tmp:=tree[k].r; tree[k].r:=tree[tmp].l; tree[tmp].l:=k;
update(k); update(tmp); k:=tmp;
end;
procedure insert(var k:longint; x:longint);  //插入新节点
begin
if (k=0)
then begin
inc(cnt); k:=cnt;
tree[k].size:=1; tree[k].w:=1;
tree[k].sum:=x; tree[k].val:=x;
tree[k].rnd:=random(10000);
tree[k].l:=0; tree[k].r:=0;
exit;
end;
inc(tree[k].size); inc(tree[k].sum,x);
if tree[k].val=x then begin inc(tree[k].w); exit; end;
if x>tree[k].val then begin
insert(tree[k].r,x);
if tree[tree[k].r].rnd<tree[k].rnd then left_rotation(k);
exit;
end;
if x<tree[k].val then begin
insert(tree[k].l,x);
if tree[tree[k].l].rnd<tree[k].rnd then right_rotation(k);
end;
end;
function query(k,val:longint):longint;  //查找中位数并记录相关值
begin
if val<=tree[tree[k].l].size
then exit(query(tree[k].l,val));
if (val>tree[tree[k].l].size+tree[k].w)
then begin
inc(tmp,tree[tree[k].l].sum+tree[k].w*tree[k].val);
inc(num,tree[tree[k].l].size+tree[k].w);
exit(query(tree[k].r,val-tree[tree[k].l].size-tree[k].w));
end;
inc(tmp,tree[tree[k].l].sum);
inc(num,tree[tree[k].l].size);
exit(tree[k].val);
end;
procedure prep;  //预先算出每个区间内的答案
var i,j,ave:longint;
begin
for i:=1 to n do begin  //分别以1~n为根建树
cnt:=0; root:=0; tot:=0;
for j:=i to n do begin
inc(tot,h[j]);
insert(root,h[j]);
tmp:=0; num:=0;
ave:=query(root,(j-i+2) div 2);
inc(ans[i,j],num*ave-tmp);
inc(ans[i,j],tot-tmp-(j-i+1-num)*ave);
end;
end;
end;
procedure main;
var i,a,b:longint;
begin
for i:=1 to m do begin
readln(a,b);
anstot:=anstot+ans[a,b];
end;
writeln(anstot);
end;
begin
init;
prep;
main;
end.


参考资料:

1.Vijos 车展-yywyzdzr-博客频道-CSDN.NET

2.【vijos1459】车展|treap|中位数|HZWER:WE are OIers
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: