poj 3368 Frequent values(非常变…

http://poj.org/problem?id=3368

Frequent values
Time Limit: 2000MSMemory Limit: 65536K
Total Submissions: 10191Accepted: 3732

Description

You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most frequent value among the integers ai , ... , aj.

Input

The input consists of several test cases. Each test case starts with a line containing two integers n and q (1 ≤ n, q ≤ 100000). The next line contains n integers a1 , ... , an (-100000 ≤ ai ≤ 100000, for each i ∈ {1, ..., n}) separated by spaces. You can assume that for each i ∈ {1, ..., n-1}: ai ≤ ai+1. The following q lines contain one query each, consisting of two integers i and j (1 ≤ i ≤ j ≤ n), which indicate the boundary indices for the
query.

The last test case is followed by a line containing a single 0.

Output

For each query, print one line with one integer: The number of occurrences of the most frequent value within the given range.

Sample Input

10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0

Sample Output

1
4
3

Source

出处:http://www.cnblogs.com/183zyz/archive/2011/05/11/2043670.html

题意:

给n个数,已经按从大到小顺序排列好,一共有q个询问,每次询问一个区间,问这个区间中出现次数最多的数是什么。
题目数据范围:
数的个数,1 <= n <= 100000
询问次数,1 <= q <= 100000
每个数的大小,-100000 <= ai <= 100000
很容易想到建立线段树,并在线段树的每个节点中保存区间中出现次数最多的数
需要解决的问题,两个子结点的信息如何合并到父节点。
很显然,子结点中出现次数最多的数不一定就是父节点中出现次数最多的数,
有可能一个数在两个子结点中的出现次数都不是最多,但是子结点合并成父节点后,这个数的出现次数就最多了
考虑到题目中的重要条件,数组中的数是有序的!
当左子节点区间最右的数与右子区间最左的数相等时,这个相等的数的出现次数可能最多
需要记录节点区间最左和最右的数的信息
poj <wbr>3368 <wbr>Frequent <wbr>values(非常变形)
我的想法就是定义一个结构体:
struct node{
int l,r;
int count,num;
int lcount,lnum;
int rcount,rnum;
};
l,r存该节点的边界。 count存该节点中出现最多的数字的个数,num存该节点中出现最多的数字。
lcount 存该节点左端连续出现的数字的个数, lnum存该节点左端连续出现的数字。
rcount 存该节点右端连续出现的数字的个数, rnum存该节点右端连续出现的数字。
建树的时候初始化的时候比较麻烦一点, 每次询问的时候不是很麻烦。。
# include
# include
# define N 100005
struct node
{
  int r,l;//l,r存该节点的边界
  int num,count;//count存该节点中出现最多的数字的个数,num存该节点中出现最多的数字。
  int rnum,rcount;//rcount 存该节点右端连续出现的数字的个数, rnum存该节点右端连续出现的数字。
  int lnum,lcount;//lcount 存该节点左端连续出现的数字的个数, lnum存该节点左端连续出现的数字。
}tree[4*N];
int a[N],MAX;
void bulid(int l,int r,int t)//建树
{
  int mid,ans;
  tree[t].l=l;
  tree[t].r=r;
  if(l==r)
  {
    tree[t].count=tree[t].lcount=tree[t].rcount=1;
    tree[t].num=tree[t].lnum=tree[t].rnum=a[l];
    return;
  }
  mid=(l+r)/2;
  bulid(l,mid,2*t);
  bulid(mid+1,r,2*t+1);
  if(tree[2*t].count>=tree[2*t+1].count) //左边结点出现最多的数字的个数大于右边,更新父亲结点
  {
    tree[t].num=tree[2*t].num;
    tree[t].count=tree[2*t].count;
  }
  else
  {
    tree[t].num=tree[2*t+1].num;
    tree[t].count=tree[2*t+1].count;
  }
  tree[t].lnum=tree[2*t].lnum;
  tree[t].lcount=tree[2*t].lcount;
  tree[t].rnum=tree[2*t+1].rnum;
  tree[t].rcount=tree[2*t+1].rcount;
  if(tree[2*t].rnum==tree[2*t+1].lnum)//节点右端连续出现的数字与左端的数字相同
  {
    ans=tree[2*t].rcount+tree[2*t+1].lcount;//用ans保存最大数字的个数
    if(ans>tree[t].count)
    {
      tree[t].count=ans;
      tree[t].num=tree[2*t].rnum;
    }
    if(tree[2*t+1].lnum==tree[2*t].lnum) tree[t].lcount+=tree[2*t+1].lcount;
    if(tree[2*t].rnum==tree[2*t+1].rnum) tree[t].rcount+=tree[2*t].rcount;
  }
}
void updata(int l,int r,int t)
{
  int mid,ans1,ans2;
  if(tree[t].l==l && tree[t].r==r)
  {
    if(tree[t].count>MAX) MAX=tree[t].count;
    return;
  }
  if(r<=tree[2*t].r) updata(l,r,2*t);
  else if(l>=tree[2*t+1].l) updata(l,r,2*t+1);
  else
  {
    mid=tree[2*t].r;
    updata(l,mid,2*t);
    updata(mid+1,r,2*t+1);
    if(tree[2*t].rnum == tree[2*t+1].lnum)
    {
      if(a[l]!=tree[2*t].rnum) ans1=tree[2*t].rcount;
      else ans1=mid-l+1;
      if(a[r]!=tree[2*t+1].lnum) ans2=tree[2*t+1].lcount;
      else ans2=r-mid;
      if(ans1+ans2 > MAX) MAX=ans1+ans2;
    }
  }
}
int main()
{
  int i,Q,start,end,n;
  while(scanf("%d",&n)!=EOF && n)
  {
    scanf("%d",&Q);
    for(i=1;i<=n;i++)
      scanf("%d",&a[i]);
    bulid(1,n,1);
    while(Q--)
    {
      scanf("%d%d",&start,&end);
      if(start==end)
      {
        printf("1\n");
        continue;
      }
      MAX=0;
      updata(start,end,1);
      printf("%d\n",MAX);
    }
  }
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值