POJ_3368_Frequent values_线段树/ST稀疏表

2 篇文章 0 订阅
2 篇文章 0 订阅
头疼

题意

给一个n长度的a int数组,且a数组为不下降数列,询问q次,每次询问l到r之间出现次数最多的数字出现了多少次。

IO

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.

分析

一开始一看这题询问区间内值的分布情况,就想用主席树,结果到了询问的时候仔细想想又发现主席树破不了。
总是出现这样的问题就是过于注重分析如何维护数据结构保持consistant,而不认真地想想数据结构维护什么数据才能得到答案。
这题把问题转化成了RMQ问题,计算每个数值出现的次数,然后把这个数组按照数字出现的顺序排成一个新的数组,并且记录原数组中每个位置对应新数组中哪个位置,记录新数组每个数对应原数组的区间范围,由于是不下降数列,这些很自然。
然后线段树或者ST稀疏表维护RMQ信息,对于一次查询l,r,先修改l和r对应新数组位置的值,因为可能正好卡在一串相等数字的中间,然后查询对应区间的RMQ信息,再把信息改回来。
还有另一种办法是比较l位置和l位置值结束位置、r位置和r位置值开始位置、l值结束位置和r值开始位置之间的RMQ信息,这样就不需要更新操作。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MXN 100010
int da[MXN];
int n,q;
int a[MXN],st[MXN],ed[MXN],mp[MXN];
int cnt;
int ll[MXN<<2],rr[MXN<<2],mx[MXN<<2];
void build(int id,int l,int r){
    ll[id]=l,rr[id]=r;
    if(l==r){
        mx[id]=da[l];
        return;
    }
    int m=(l+r)>>1,ls=id<<1,rs=ls|1;
    build(ls,l,m);
    build(rs,m+1,r);
    mx[id]=max(mx[ls],mx[rs]);
}
void update(int id,int loc,int num){
    if(ll[id]==rr[id]){
        mx[id]=num;
        return;
    }
    int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
    if(loc<=m)  update(ls,loc,num);
    else    update(rs,loc,num);
    mx[id]=max(mx[ls],mx[rs]);
}
int query(int id,int l,int r){
    if(ll[id]==l&&rr[id]==r)    return mx[id];
    int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
    if(r<=m)    return query(ls,l,r);
    else if(l>m)    return query(rs,l,r);
    else    return max(query(ls,l,m),query(rs,m+1,r));
}
int main(){
    while(scanf("%d",&n)!=EOF&&n){
        scanf("%d",&q);
        cnt=-1;
        int pre=-1000000;
        for(int i=1;i<=n;++i){
            scanf("%d",a+i);
            if(a[i]!=pre){
                if(pre!=-1000000){
                    ed[cnt]=i-1;
                }
                ++cnt;
                da[cnt]=0;
                st[cnt]=i;
                pre=a[i];
            }
            mp[i]=cnt;
            ++da[cnt];
        }
        ++cnt;
        build(1,0,cnt-1);
        for(int Q=0;Q<q;++Q){
            int l,r;
            scanf("%d%d",&l,&r);
            int tl=mp[l],tr=mp[r];
            if(tl==tr){
                printf("%d\n",r-l+1);
                continue;
            }
            update(1,tl,ed[tl]-l+1);
            update(1,tr,r-st[tr]+1);
            printf("%d\n",query(1,tl,tr));
            update(1,tl,ed[tl]-st[tl]+1);
            update(1,tr,ed[tr]-st[tr]+1);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值