【Vijos1459】车展

【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;
  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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值