poj 3368 Frequent values(区间合并)

题意:一个长度为n的数组,进行m次询问,每次询问给出一个区间,求出这个区间中的数出现的最多的次数


题目链接:http://poj.org/problem?id=3368


思路:线段树,区间合并

如下:

线段树中的节点记录出现的最左边的数和次数,以及最右边的数和次数,

在合并的时候,如下两个区间 

[s,m] [m+1,t] 

那么需要看val[m] 和val[m+1] 的值是不是相同,

不相同的话,直接合并即可,

否则,

那么左边和最右边的值和次数均会发生改变,进行比较


#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 100010
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct segnode{
    int lnum,rnum,num;
};
segnode tree[maxn<<2];

int val[maxn];


void build(int l,int r,int rt){
    if(l == r){
        tree[rt].lnum = 1;
        tree[rt].rnum = 1;
        tree[rt].num = 1;
        return ;
    }
    int m = (l + r)/2;
    build(lson);
    build(rson);

    if(val[m] == val[m+1]){
        if(val[l] == val[m + 1])
             tree[rt].lnum = tree[rt<<1].lnum + tree[rt<<1|1].lnum;
        else tree[rt].lnum = tree[rt<<1].lnum;

        if(val[m] == val[r])
             tree[rt].rnum = tree[rt<<1|1].rnum + tree[rt<<1].rnum;
        else tree[rt].rnum = tree[rt<<1|1].rnum;

        tree[rt].num = max(tree[rt<<1].num,max(tree[rt<<1|1].num, tree[rt<<1].rnum + tree[rt<<1|1].lnum));
    }
    else{
        tree[rt].lnum = tree[rt<<1].lnum;
        tree[rt].rnum = tree[rt<<1|1].rnum;
        tree[rt].num = max(tree[rt<<1].num , tree[rt<<1|1].num);
    }
}

int query(int L,int R,int l,int r,int rt){
    if(L <= l && r <= R){
        return tree[rt].num;
    }

    int m = (l + r)/2;
    if(R <= m) return query(L,R,lson);
    else if(L > m) return query(L,R,rson);
    else{
        int lans = query(L,m,lson);
        int rans = query(m+1,R,rson);
        if(val[m] == val[m+1]){
            int temp = min(tree[rt<<1].rnum , m - L + 1) + min(tree[rt<<1|1].lnum , R - m);
            return max(temp,max(lans,rans));
        }
        else{
            return max(lans,rans);
        }
    }
}

int main()
{
    int n,m,a,b;
    while(~scanf("%d",&n) && n){
        scanf("%d",&m);
        for(int i=1;i<=n;i++)
        scanf("%d",&val[i]);

        build(1,n,1);
        while(m--){
            scanf("%d %d",&a,&b);
            if(a==b) printf("1\n");
            else
                printf("%d\n",query(a,b,1,n,1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值