poj 3368 Frequent values 线段树

题目大意

给一个长度为n的不降序列a1,a2,a3,…,an,有q个询问,每个询问为:i j

询问在子序列ai…aj中出现最多的元素。

数据范围:1 <= n, q <= 100000

 

分析

  注意到题目描述中的“不降序列”,让我们联想到可以使用线段树这一数据结构。

  对于线段树的每一个节点:

  记录left[k]表示当前区间最左边连续的元素有多少个

  记录right[k]表示当前区间最右边连续的元素有多少个

  记录max[k]表示当前区间中出现最多的元素有多少(当然它们是连续的)

  当然,还要储存这些区间是什么数。

  对于每一次询问,则可用类似方法,得到区间[i,j]中最左边连续元素的个数,最右边连续元素的个数,以及最多的连续元素的个数。

自己见代码

代码

  

type
  pnode=^tnode;
  tnode=record
    lc,rc:pnode;
    a,b,c:longint;
    m1,m2,m3:longint;
end;

type
  arr=record
    a,b,c:longint;
    m1,m2,m3:longint;
end;

var
  t:pnode;
  i,j,k:longint;
  x,y:longint;
  n,m:longint;
  ans:arr;
  f:array[0..100002] of longint;

procedure neww(var t:pnode);
begin
  if t=nil then
    begin
      new(t);
      t^.c:=0;
      t^.lc:=nil;
      t^.rc:=nil;
    end;
end;

function max(x,y:longint):longint;
begin
  if x>y then exit(x)
         else exit(y);
end;

procedure insert(var t:pnode; l,r:longint);
var
  i,j,k:longint;
  mid:longint;

begin
  with t^ do
    begin
      {if c=0 then
        begin   }
          mid:=(l+r) div 2;
          if l=r
            then
              begin
                a:=1; b:=1; c:=1;
                m1:=f[l]; m2:=f[l]; m3:=f[l];
                exit;
              end;
          neww(lc);
          neww(rc);
          insert(lc,l,mid);
          insert(rc,mid+1,r);
          c:=max(lc^.c,rc^.c);
          if c=lc^.c then m3:=lc^.m3
                     else m3:=rc^.m3;
          if lc^.m2=rc^.m1
            then
              begin
                c:=max(c,lc^.b+rc^.a);
                m3:=lc^.m2;
              end;
          a:=lc^.a;
          m1:=lc^.m1;
          if lc^.m1=rc^.m1
            then a:=a+rc^.a;
          b:=rc^.b;
          m2:=rc^.m2;
          if lc^.m2=rc^.m2
            then b:=b+lc^.b;
    end;
    {end;}
end;

function find(t:pnode;l,r,x,y:longint):arr;
var
  mid:longint;
  l1,r1:arr;
begin
  if t=nil then exit;
  with t^ do
    begin
      mid:=(l+r) div 2;
      if (l=x) and (r=y)
        then
          begin
            find.a:=a; find.b:=b; find.c:=c;
            find.m1:=m1; find.m2:=m2; find.m3:=m3;
            exit;
          end;
      if (l<=x) and (mid>=y)
        then
          begin
            find:=find(lc,l,mid,x,y);
            exit;
          end;
      if (mid<x) and (r>=y)
        then
          begin
            find:=find(rc,mid+1,r,x,y);
            exit;
          end;
      l1:=find(lc,l,mid,x,mid);
      r1:=find(rc,mid+1,r,mid+1,y);
      with find do begin
        c:=max(l1.c,r1.c);
        if c=l1.c then m3:=l1.m3
                   else m3:=r1.m3;
        if l1.m2=r1.m1
          then
            begin
              c:=max(c,l1.b+r1.a);
              m3:=l1.m2;
            end;
        a:=l1.a;
        m1:=l1.m1;
        if l1.m1=r1.m1
          then a:=a+r1.a;
        b:=r1.b;
        m2:=r1.m2;
        if l1.m2=r1.m2
          then b:=b+l1.b;
      end;
    end;
end;

begin
  while true do
  begin
  read(m);
  if m=0 then break;
  fillchar(f,sizeof(f),0);
  dispose(t);
  readln(n);
  for i:=1 to m do
    read(f[i]);
  readln;
  fillchar(t,sizeof(t),0);
  neww(t);
  insert(t,1,m);
  for i:=1 to n do
    begin
      readln(x,y);
      ans:=find(t,1,m,x,y);
      writeln(ans.c);
    end;
  end;
end.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值