poj3368(线段树)

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

Frequent values
Time Limit: 2000MSMemory Limit: 65536K
Total Submissions: 10192Accepted: 3733

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

参考大神们的代码

查询某个区间内连续出现次数最多的一个数出现的次数

以前做的时候,没有记录左边连续最多的次数,右边连续最多的次数,而是记录了出现最多的数的值,写的很长,还不知道WA哪了

前两天经队友点拨,发现对于这种求最长连续区间的问题,记录左边连续最多的次数(lv),右边连续最多的次数(rv),以及整个区间连续最多次数(val),更新和查询的时候会很方便

首先,如果左子区间的lv等于左子区间的长度且可以延伸到右子区间,那么父区间的lv值等于左子区间的lv加上右子区间的lv,否则,父区间的lv值就是左子区间的lv值,对于右子区间,同理

对于父区间的val值,应该是先在左右子区间的val值中取一个较大值,另外,如果左右区间能够延续,则在当前值和左子区间的rv+右子区间的lv之间取较大值

还有一个特殊情况就是在查询的时候,如果要分别在左子区间和右子区间查找,结果分别为x,y,如果左右两个区间不能延续,那肯定是在x,y中找一个最大值,如果可以延续的话,设延续部分的最大次数为z,那么,就要在x,y,z中取一个最大值,另外,求z时要比较小心,这个区间的左端点要在向左延伸的最远点和查询区间的左端点中取一个较大值,这个区间的有端点要在向右延伸的最远点和查询区间的有端点间取一个较小值,详情见代码

 

 

 思路:在线段树的结点内设5个变量l、r、mx、lf、rf,[l,r]表示该结点的区间范围,
* lf和rf分别表示元素a[l]和a[r]在区间内的出现频率,mx表示区间内的最高出现频率。
* 假设区间[x,y]和[y+1,z]均被询问[i,j]覆盖,则可以分情况讨论区间[x,z]的mx值:
* 若a[y]==a[y+1],则mx[x,y]=max{mx[x,y],mx[y+1,z],rf[x,y]+lf[y+1,z]}
* 否则mx[x,y]=max{mx[x,y],mx[y+1,z]}

 

#include
using namespace std;
#define MAX 100000
#define min(a,b) (a
struct NODE
{
 int l,r;//[l,r]表示该结点的区间范围,
 int fl,fr;//该区间最左端元素和最右端元素出现频率
 int mx;//该区间最高频率
};
int s[MAX+2];
NODE tree[MAX*3];
 void build(int v,int l,int r)
 {
     tree[v].l = l;
     tree[v].r = r;
     if(l ==r )
     {
         tree[v].fl = 1;
         tree[v].fr = 1;
         tree[v].mx = 1;
         return;
     }
     int mid = (l+r)>>1;
     build(v*2,l,mid);
     build(v*2+1,mid+1,r);
     tree[v].fl = tree[v*2].fl;
     tree[v].fr = tree[v*2+1].fr;
     //左孩子最右边元素和右孩子最左边元素相同
     if(s[tree[v*2].r] == s[tree[v*2+1].l])
     {
         tree[v].mx = (tree[v*2].fr + tree[v*2+1].fl);
         //当前节点的最右边元素和右孩子坐左边元素相同,就说明右孩子所有元素相同并且和
         //左孩子右边部分元素相同,那么当前节点的右频率 = (tree[v*2].fr + tree[v*2+1].fl);
         if(s[tree[v].r] == s[tree[v*2+1].l])
         {
             tree[v].fr = tree[v].mx;
         }
         //原理同上
         if(s[tree[v].l] == s[tree[v*2].r])
         {
             tree[v].fl = tree[v].mx;
         }
 
    }
  else
     {
         tree[v].mx  = 1;
     }
     if(tree[v*2].mx > tree[v].mx  )
         tree[v].mx  = tree[v*2].mx;
     if(tree[v*2+1].mx > tree[v].mx  )
         tree[v].mx  = tree[v*2+1].mx;
 
}
 int find(int v,int x,int y)
 {
     if(x<= tree[v].l && y >= tree[v].r)
         return tree[v].mx;
    //询问区间在左孩子
     if(y <= tree[v*2].r)
     {
         return find(v*2,x,y);
     }
     //询问区间在右孩子
     if(x >= tree[v*2+1].l)
         return find(v*2+1,x,y);
    int max = 1,max1,max2;
     //左孩子最右边元素和右孩子最左边元素相同
     if(s[tree[v*2].r] == s[tree[v*2+1].l])
     {
         //询问区间有可能未把tree[v*2].fr个元素全包含进去,所以要取min(tree[v*2].r-x+1, tree[v*2].fr)
         max =min(tree[v*2].r-x+1, tree[v*2].fr) + min(y - tree[v*2+1].l+1,tree[v*2+1].fl);
     }
     max1 = find(v*2,x,tree[v*2].r);
     max2 = find(v*2+1,tree[v*2+1].l,y);
     if(max1 > max)
         max = max1;
     if(max2 > max)
         max = max2;
     return max;
}
 int main()
 {
    // freopen("in","r",stdin);
     int i;
     int n,q;
     int x,y;
     while(scanf("%d %d",&n,&q)&&n)
     {
         for(i = 1; i <= n; i++)
             scanf("%d",s+i);
         build(1,1,n);
         while(q--)
         {
             scanf("%d %d",&x,&y);
             printf("%d\n",find(1,x,y));
         }
     }
    return 0;
 }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值