POJ 3368 Frequent values(线段树)

题意:给出一个大小为n的非降序数字序列,查询区间内出现次数最多的数字出现的次数

思路:如何建树比较难想,建树前需要一些处理

   观察一下,数字的范围为-100000-100000,给出的顺序非降序

           让每个出现过的数字对应一个区间,a[ ]记录序列,

           th[ ] 记录某个位置的数字是出现的第几个数字,cnt[ ]记录这个数字出现的次数,用cnt[ ] 建线段树记录最大值,l[ ] 记录这个数字出现的第一个位置,r[ ]记录出现的最后一个位置

           查询时将区间分为三个部分:

一、th[i] + 1 到 th[j] - 1 这几个数字的cnt最大值,查询线段树;

二、th[i] 这个数在查询区间出现的次数,r [ th[ i ] ] - i + 1

三、 th[j]这个数在查询区间出现的次数,j - l [ th[ j ] ]  + 1

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define lson l, m, rt * 2
#define rson m + 1, r, rt * 2 + 1
int a[100010], th[100010], cnt[100010];
int l[100010], r[100010];
int max[100010 * 4];
void pushup(int rt)
{
    max[rt] = max[rt * 2] > max[rt * 2 + 1] ?
                max[rt * 2] : max[rt * 2 + 1];
}
void build(int l, int r, int rt)
{
    if(l == r)
    {
        max[rt] = cnt[l];
        return;
    }
    int m = (l + r) / 2;
    build(lson); build(rson);
    pushup(rt);
}
int query(int L, int R, int l, int r, int rt)
{
    if(L <= l && r <= R)
    {
        return max[rt];
    }
    int m = (l + r) / 2;
    int lmax = 0, rmax = 0;
    if(L <= m) lmax = query(L, R, lson);
    if(R > m) rmax = query(L, R, rson);
    return lmax > rmax ? lmax : rmax;
}
int main()
{
    #ifdef LOCAL
    freopen("data.in", "r", stdin);
    #endif

    int n, q;
    while(scanf("%d", &n) != EOF)
    {
        if(n == 0) break;
        scanf("%d", &q);
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        memset(th, 0, sizeof(th));
        memset(cnt, 0, sizeof(cnt));
        memset(l, 0, sizeof(l));
        memset(r, 0, sizeof(r));
        memset(max, 0, sizeof(max));
        th[0] = 1; cnt[1] = 1; l[1] = 0; r[1] = 0;
        int k = 1;
        for(int i = 1; i < n; i++)
        { //得到th[],cnt[],l[],r[]
            if(a[i] == a[i - 1])
            {
                th[i] = k;
                cnt[k]++; r[k] = i;
            }
            else
            {
                k++; th[i] = k;
                cnt[k] = 1; l[k] = i; r[k] = i;
            }
        }
        build(1, k, 1);
        int x, y, ans;
        while(q--)
        {
            scanf("%d%d", &x, &y);
            x--; y--;
            if(th[x] == th[y])
            {
                ans = y - x + 1;
                //printf("1:%d %d: %d\n", x, y, ans);
            }
            else if(th[x] + 1 == th[y])
            {
                ans = (r[th[x]] - x + 1) > (y - l[th[y]] + 1) ?
                        (r[th[x]] - x + 1) : (y - l[th[y]] + 1);
                //printf("2:%d %d: %d\n", x, y, ans);
            }
            else
            { //分三部分查询
                ans = query(th[x] + 1, th[y] - 1, 1, k, 1);
                if(r[th[x]] - x + 1 > ans) ans = r[th[x]] - x + 1;
                if(y - l[th[y]] + 1 > ans) ans = y - l[th[y]] + 1; 
                //printf("3:%d %d: %d\n", x, y, ans);
            }
            printf("%d\n", ans);
        }
    }

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值