宁波赛 2010 T3 插入排序 线段树

【题目描述】

    有依次排列的一列数a1,a2,a3,…,an-1,an。你可以随便把一个数拿出,插到最前面(当前第1个数a1前)、最后面(当前最后一个数an后面)、或者剩余数列中任意的相邻两个数之间。
比如起始数依次为4 5 6 7 8 9。如果把第4个数a4=7拿出,然后任意放回,可能有
7 4 5 6 8 9
4 7 5 6 8 9
4 5 7 6 8 9
4 5 6 7 8 9
4 5 6 8 7 9
4 5 6 8 9 7
这6种排列。
已知把第i个数ai拿出后插回去花费的代价为该数的值ai。小猪希望花费最少的代价来把这个数列排成不降序列。所谓不降序列,是指对于数列中任意两个数,排在前面的数小于等于排在后面的数。

【输入】

      输入文件insert.in的第一行只有一个整数n,表示共有n个整数。
第2行有n个整数(互相之间以一个空格分隔),表示待排序的n个数。
【输出】

输出文件insert.out中只有一行,该行只有一个整数,表示花费的最小代价。

【样例输入】

4

7 1 2 3

【样例输出】

6

【样例说明】

很显然移动7是不划算的。一种移动方法是:
初始情况:7 1 2 3  => (把3移到最前面得) 3 7 1 2
=> (把1移到最前面得) 1 3 7 2
=> (把2移到1与3之间得) 1 2 3 7 最终整个序列成为升序。
消耗的代价是3 + 1 + 2 = 6。
【数据说明】

30%的数据初始数列是1至n的一个排列,即1至n都在初始数列出现且仅出现一次;
20%的数据,1≤n≤10;
90%的数据,1≤n≤1000;
100%的数据,1≤n≤100000,初始数列的每个数在1和20000之间(包括1和20000)。

 

此题如果看得出来,比赛时90分随便拿了。

其实这题就是求sum{a[]}减去max(LISS(Longest Increasing Subsequence Sum))的值。

(讨厌我英文缩写的勿喷)

因为移动内容最小,就是保留最大。

那么其实O(N^2)的最长不下降子序列和1分钟OK了,也就是90分。

如何满分?用到线段树。把[1,max{a[]}]作为线段树的分割目标。

然后DP的时候在tree里寻找[1,a[i]]中的最大值。这样优化为了O(N*LogN)的算法。

网上也有树状数组的方法。个人喜欢线段树。

宁波赛的题果然厉害。

(代码Ugly,大牛勿喷)

 

var a,f,tree:array[0..100001] of longint;
    n,i,sum,maxlen,maxnum,ans:longint;
function max(a,b:longint):longint;
begin
  if a>b then exit(a);
  exit(b);
end;
procedure insert(id,left,right:longint);
var mid:longint;
begin
  if left=right then
  begin
    if right=a[i] then
      tree[id]:=max(tree[id],f[i]);
    exit;
  end;
  mid:=(left+right) div 2;
  if a[i]<=mid then insert(id*2,left,mid)
               else insert(id*2+1,mid+1,right);
  tree[id]:=max(tree[id*2],tree[id*2+1]);
end;
function query(id,left,right:longint):longint;
var t1,t2,mid:longint;
begin
  if (left>a[i]) then exit(-1);
  if (right<=a[i]) then exit(tree[id]);
  mid:=(left+right) div 2;
  t1:=query(id*2,left,mid);
  t2:=query(id*2+1,mid+1,right);
  if t1=-1 then exit(t2);
  if t2=-1 then exit(t1);
  exit(max(t1,t2));
end;
begin
  readln(n);
  sum:=0;
  maxnum:=0;
  ans:=0;
  for i:=1 to n do
  begin
    read(a[i]);
    sum:=sum+a[i];
    if maxnum<a[i] then maxnum:=a[i];
  end;
  for i:=1 to n do
  begin
    maxlen:=query(1,1,maxnum);
    f[i]:=maxlen+a[i];
    insert(1,1,maxnum);
    ans:=max(ans,f[i]);
  end;
  writeln(sum-ans);
end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值