树状数组求Thair对数

<span style="color: rgb(53, 95, 41); font-family: Tahoma; line-height: 18px; ">Thair问题
【问题描述】
在含有N个数的序列A[1]..A[N]中,三个数被称作"Thair"当且仅当i<j<k且A[i]<A[j]<A[k]。
求一个序列中"Thair"的个数。
【输入文件】
输入文件名为thair.in。
输入文件第一行包含一个正整数N。
接下来一行N个数表示A[1]..A[N]。
【输出文件】
输出文件名为thair.out。输出文件只有一个数字,即序列中"Thair"的个数。
【输入样例】
5
1 2 2 3 4
【输出样例】
7
【时间限制】1s
【空间限制】128MB
【数据规模】
20%的数据满足N ≤ 500;
60%的数据满足N ≤ 5000;
100%的数据满足N ≤ 100000, 0≤A[i]≤2^31-1。

先来说说比较容易想到的想法:
O(n^3) 20分
直接枚举3个数,然后判断3个数是否构成“Thair关系”,代码复杂度很低,分分钟敲完。20分。
O(n^2) 60分
枚举i和j,不枚举k,而是在前面先预处理一下:即枚举i,然后统计一下i+1~n有多少大于a[i]的,然后在枚举i,j的时候直接可以统计方案数了。还不错,能拿60。
O(nlogn) 100分
满分的想法。首先,我们要明白我们所求的是Σ(i=1,n) f[i]*g[i]。
其中,f[i]是指以i结尾,前面有几个比a[i]小的元素,用以统计(i<j && a[i]<a[j])部分;
而g[i]表示以i开头,后面有几个比a[i]大的元素,用于统计(j<k && a[j]<a[k])部分;
那么,我们要做的就是(i<j && a[i]<a[j])等这样的部分。那么,什么算法或者是数据结构能胜任呢?
其实也许有很多都可以解掉这个经典的题目,不过树状数组对于这题还是不错的。
首先,我们需要离散。因为a[i]太大了,大到maxlongint,而n只有100000。那么,离散一下,最多也只有100000个元素。
然后,通过更新树和查询树可得到f和g数组。时间是log级别的(树状数组不懂滴童鞋,请baidu)。
但是像我们这样做,需要调2次排序,序列还要反着做一次,代码复杂度也是比较高滴。。。
</span>

const maxn=100005;
var
  a,b,c,id,ref:array[0..maxn] of longint;//ref(reflection)数组,即映射数组,用来离散
  f,g:array[0..maxn] of int64;//f[i]表示以i结尾,前面有几个比a[i]小的元素;
                              //g[i]表示以i开头,后面有几个比a[i]大的元素;
  n,m:longint; ans:int64;//n<=100000,由于我们要记录sigma(i=1,n) f[i]*g[i]的值,最多会超出maxlongint的上限;
procedure init;
var i:longint;
begin
  readln(n);
  for i:=1 to n do
  begin
    read(a[i]);
    id[i]:=i;
  end;
  b:=a;
end;
function lowbit(now:longint):longint;
begin
  exit(now and (-now));
end;
procedure update(now:longint);
begin
  while (now<=n) do
  begin
    inc(c[now]);
    inc(now,lowbit(now));
  end;
end;
function query(now:longint):longint;
var sum:longint;
begin
  sum:=0;
  while (now>0) do
  begin
    inc(sum,c[now]);
    dec(now,lowbit(now));
  end;
  exit(sum);
end;
//以上是树状数组的3个基本操作
procedure swap(var x,y:longint);
var t:longint;
begin
  t:=x; x:=y; y:=t;
end;
procedure qsort_gosmall(L,R:longint);
var i,j,mid:longint;
begin
  i:=L; j:=R; mid:=a[L+random(R-L+1)];
  repeat
    while a[i]>mid do inc(i);
    while a[j]<mid do dec(j);
    if i<=j then
    begin
      swap(a[i],a[j]); swap(id[i],id[j]);
      inc(i); dec(j);
    end;
  until i>j;
  if i<R then qsort_gosmall(i,R);
  if L<j then qsort_gosmall(L,j);
end;
procedure qsort_golarge(L,R:longint);
var i,j,mid:longint;
begin
  i:=L; j:=R; mid:=a[L+random(R-L+1)];
  repeat
    while a[i]<mid do inc(i);
    while a[j]>mid do dec(j);
    if i<=j then
    begin
      swap(a[i],a[j]); swap(id[i],id[j]);
      inc(i); dec(j);
    end;
  until i>j;
  if i<R then qsort_golarge(i,R);
  if L<j then qsort_golarge(L,j);
end;
procedure ca_front;
var i:longint;
begin
  qsort_gosmall(1,n);//将数组从大到小排序,以便于树状数组累计
  fillchar(ref,sizeof(ref),0); m:=0;
  for i:=1 to n do
  if a[i]<>a[i-1] then
  begin
    inc(m);
    ref[id[i]]:=m;
  end
  else ref[id[i]]:=m; //离散的思想,由于题目给出的条件两书不能相等,
                      //所以要做“去重”工作,但并非真正的去重
  fillchar(c,sizeof(c),0);
  for i:=1 to n do
  begin
    update(ref[i]);
    f[i]:=i-query(ref[i]);
  end;
end;
procedure ca_behind;
var i:longint;
begin
  for i:=1 to n do id[i]:=i;      //
  fillchar(ref,sizeof(ref),0);    //
  m:=0; a[0]:=0;                  //
  for i:=1 to n do a[i]:=b[n+1-i];//恢复原数组
  qsort_golarge(1,n);//与上同理
  for i:=1 to n do
  if a[i]<>a[i-1] then
  begin
    inc(m);
    ref[id[i]]:=m;
  end
  else ref[id[i]]:=m;
  fillchar(c,sizeof(c),0);
  for i:=1 to n do
  begin
    update(ref[i]);
    g[n-i+1]:=i-query(ref[i]);
  end;
end;
procedure calc;
var i:longint;
begin
  ans:=0;
  for i:=1 to n do inc(ans,(f[i]*g[i]));//即计算我们所求的sigma(i=1,n) f[i]*g[i]
end;
procedure print;
begin
  writeln(ans);
end;
begin
  init;//初始化
  ca_front;//计算(i<j && a[i]<a[j])部分
  ca_behind;//计算(j<k && a[j]<a[k])部分
  calc;//计算方案总数
  print;//输出
end.

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值