【Vijos1459】车展

来源:互联网 发布:中国电信4g网络制式 编辑:程序博客网 时间:2024/06/10 09:00

【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 44 1 2 13 0 91 52 63 42 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 0
原创粉丝点击