Hrbust-1830 第一个重复出现的数(线段树区间最值查询)

Description
给出n个数,问你在区间[a,b]内从右到左第一个出现重复的数字是哪一个。

Input
每组样例第一行给出n,表示有n个数(3≤ n ≤ 500,000)。

第二行给出n个数,每个数不大于2^31。

然后给出一个Q,表示有Q次询问 (1 ≤ Q ≤ 50,000)。

接下来Q行每行两个数a,b (1 <= a < b <= n)。

Output
输出每次询问在[a,b]区间内第一次出现重复的数字是哪个,如果没有则输出”jiong”。

每一组样例后输出一个换行。

Sample Input

5

1 2 3 1 2

3

1 4

1 5

3 5

6

1 2 3 3 2 1

4

1 4

2 5

3 6

4 6

Sample Output
1

2

jiong

3

3

3

jiong

要从在区间L到R内找到第一个重复出现的的数,并且是从R遍历到L的过程中找到的第一个数,我们预处理出每一个数的上一次出现的位序,直接按值大小sort然后遍历找位序,再按位序sort回来,建立线段树,于是在线段树我们查询区间最大值,这个最大值即区间中第一次出现的重复的数的最近位置。检查这个值是否存在,若存在检查是否在区间内,符合条件直接输出即可

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct num
{
    int i,num,next;
}a[500005];
struct node
{
    int l,r,maxx;
}tree[500005*4];
bool cmp(num a,num b)
{
    if(a.num==b.num)return a.i<b.i;
    return a.num<b.num;
}
bool cmp2(num a,num b)
{
    return a.i<b.i;
}
void build(int l,int r,int root)///建树
{
    tree[root].l=l;
    tree[root].r=r;
    tree[root].maxx=-1;
    if(l==r)
    {
        tree[root].maxx=a[l].next;
    }
    else
    {
        int mid=(l+r)/2;
        build(l,mid,root*2);
        build(mid+1,r,root*2+1);
        tree[root].maxx=max(tree[root*2].maxx,tree[root*2+1].maxx);
    }
}
int query(int l,int r,int root)
{
    if(l<=tree[root].l&&tree[root].r<=r)
    {
        return tree[root].maxx;
    }
    else
    {
        int mid = (tree[root].l+tree[root].r)/2;
        int res=-1;
        if(l<=mid)
        {
            res = max(query(l,r,root*2),res);
        }
        if(r>=mid+1)
        {
            res = max(query(l,r,root*2+1),res);
        }
        return res;///返回最大值
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i].num);
            a[i].i=i;
        }
        sort(a+1,a+n+1,cmp);///按数的大小排序,得到相同数的位序,然后错位赋值前一个相同元素的位序
        for(int i=n;i>=2;i--)
        {
            if(a[i].num==a[i-1].num) a[i].next=a[i-1].i;
            else a[i].next=-1;
        }
        sort(a+1,a+1+n,cmp2);///将打乱顺序的数列按恢复成原序列(位序排序)
        build(1,n,1);///将按顺序排列好并赋值了相同数下一位序的数组建立到线段树中
        int q,l,r;
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d%d",&l,&r);///线段树最大值查询
            int ans=query(l,r,1);
            if(ans==-1||ans<l)printf("jiong
");
            else printf("%d
",a[ans].num);
        }
        printf("
");
    }
}
原文地址:https://www.cnblogs.com/kuronekonano/p/11135793.html