POJ 3368 Frequent values (线段树)

Frequent values
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 11083   Accepted: 4063

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

题意:一个具有n(1~100000)个点的序列,从大到小排序,有一些点的大小是相等的,问区间[a,b]上,相等大小的点最多是几个。

思路:线段树+离散化。离散的运用:将连续的相等的一个区间的点集,聚集为线段树的一个节点,并且在这个节点上有信息能体现出这个区间的性质。

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int N=100010;

#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)

struct Tree{
    int l,r;
    int len;    //  len保存这个点集的点的数量
}tree[N<<2];

struct Point{
    int x,y;
}seg[N];

int num[N],hash[N];

void PushUp(int rt){
    tree[rt].len=max(tree[L(rt)].len,tree[R(rt)].len);
}

void build(int l,int r,int rt){
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r){   
        tree[rt].len=seg[l].y-seg[l].x+1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,L(rt));
    build(mid+1,r,R(rt));
    PushUp(rt);
}

int ans;

void query(int L,int R,int rt){
    if(tree[rt].l==L && tree[rt].r==R){
        ans=max(ans,tree[rt].len);
        return ;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(R<=mid)
        query(L,R,L(rt));
    else if(L>=mid+1)
        query(L,R,R(rt));
    else{
        query(L,mid,L(rt));
        query(mid+1,R,R(rt));
    }
}

int main(){

    //freopen("input.txt","r",stdin);

    int n,m;
    while(~scanf("%d",&n) && n){
        scanf("%d",&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&num[i]);
        int cnt=0,pre=999999;
        for(int i=1;i<=n;i++){   //  离散化,将点的序列离散化为一个个的点集
            if(num[i]!=pre){
                pre=num[i];
                cnt++;
                seg[cnt].x=i;
                seg[cnt].y=i;
            }else
                seg[cnt].y=i;
            hash[i]=cnt;        //  hash保存序列上第i个点,对应的点集的下标
        }
        build(1,cnt,1);
        int a,b;
        while(m--){
            scanf("%d%d",&a,&b);
            int loc1=hash[a];   //如下,好的主函数操作能很大程度上简化线段树的复杂度
            int loc2=hash[b];
            if(loc1==loc2)      //  情况1:[a,b]为一个点集
                printf("%d\n",b-a+1);
            else{               //  情况2,3:[a,b]包含多个点集
                int n1=seg[loc1].y-a+1;
                int n2=0;
                int n3=b-seg[loc2].x+1;
                if(loc2-loc1>1){    //情况3:[a,b]包含3个以上点集,中间点集最大值用到线段树
                    ans=0;
                    query(loc1+1,loc2-1,1);
                    n2=ans;
                }
                printf("%d\n",max(n1,max(n2,n3)));
            }
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/jackge/p/3041698.html