BZOJ 3489: A simple rmq problem

我以前可能写了个假的KD-Tree,关键是还过了这么多题。。。

题目大意

题目链接
给出一个长度为n的序列,给出M个询问:在[l,r]之间只出现过一次的数的最大值。要求在线,找不到输出0.

分析

确定了一个上一次出现和下一次出现的位置之后,就是在一个三元组内查询最值,那么就上KD-Tree即可。

代码

#include<cmath>
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100000+105,maxm=200000+105;
int n,m,a[maxn],lastans=0,ret;
int pos[maxn];
int D;
struct data{
    int d[3],w;//l,r是实际有效闭区间
    friend bool operator<(data a,data b)
    {
        return a.d[D]<b.d[D];
    }
}p[maxn];
struct KD_Tree
{
    int rt,np,lc[maxn],rc[maxn],mx[maxn][3],mi[maxn][3],maxv[maxn];
    void pushup(int now)
    {
        if(lc[now])
        {
            maxv[now]=max(maxv[now],maxv[lc[now]]);
            for(int i=0;i<3;i++)
            {
                mx[now][i]=max(mx[now][i],mx[lc[now]][i]);
                mi[now][i]=min(mi[now][i],mi[lc[now]][i]);
            }
        }
        if(rc[now])
        {
            maxv[now]=max(maxv[now],maxv[rc[now]]);
            for(int i=0;i<3;i++)
            {
                mx[now][i]=max(mx[now][i],mx[rc[now]][i]);
                mi[now][i]=min(mi[now][i],mi[rc[now]][i]);
            }
        }
    }
    int Newnode(int x)
    {
        ++np;
        maxv[np]=p[x].w;
        for(int i=0;i<3;i++)
            mx[np][i]=mi[np][i]=p[x].d[i];
        return np;
    }
    void Build(int &now,int L,int R,int k)
    {
        if(L>R)return;
        int m=(L+R)>>1;
        D=k;
        nth_element(p+L,p+m,p+R+1);
        now=Newnode(m);

        Build(lc[now],L,m-1,(k+1)%3);
        Build(rc[now],m+1,R,(k+1)%3);
        pushup(now);
    }
    bool calc1(int now,data i,data j)//如果now完全在ij外就返回1 
    {
        for(int k=0;k<3;k++)
        {
            if(mx[now][k]<i.d[k] || j.d[k]<mi[now][k])
                return 1;
        }
        return 0;
    }
    bool calc2(int now,data i,data j)//如果i,j包含了now就返回1 
    {
        for(int k=0;k<3;k++)
        {
            if(!(i.d[k]<=mi[now][k] && mx[now][k]<=j.d[k]))
                return 0;
        }
        return 1;
    }
    bool calc(data x,data i,data j)
    {
        for(int k=0;k<3;k++)
        {
            if(!(i.d[k]<=x.d[k] && x.d[k]<=j.d[k]))
                return 0;
        }
        return 1;
    }
    void query(int now,int L,int R,data i,data j)
    {
        if(!now)return;
        if(maxv[now]<=ret)return;
        if(calc1(now,i,j))return;
        if(calc2(now,i,j)){ret=maxv[now];return;}
        int m=(L+R)>>1;
        if(calc(p[m],i,j))
            ret=max(ret,p[m].w);
        if(maxv[lc[now]]>maxv[rc[now]])
        {
            query(lc[now],L,m-1,i,j);
            query(rc[now],m+1,R,i,j);
        }
        else
        {
            query(rc[now],m+1,R,i,j);
            query(lc[now],L,m-1,i,j);
        }

    }
}kd;
void Init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        p[i].w=a[i];
        p[i].d[2]=i;
        p[i].d[0]=pos[a[i]]+1;
        pos[a[i]]=i;
    }

    for(int i=1;i<=n;i++)pos[i]=n+1;
    for(int i=n;i;i--)
    {

        p[i].d[1]=pos[a[i]]-1;
        pos[a[i]]=i;
    }
    kd.Build(kd.rt,1,n,0);
}
void query()
{
    int x,y,L,R;
    while(m--)
    {
        scanf("%d%d",&x,&y);
        L=min((x+lastans)%n+1,(y+lastans)%n+1);
        R=max((x+lastans)%n+1,(y+lastans)%n+1);
        ret=0;
        data i,j;
        i.d[0]=0,j.d[0]=L;
        i.d[1]=R,j.d[1]=n+1;
        i.d[2]=L,j.d[2]=R;
        kd.query(kd.rt,1,n,i,j);
        lastans=ret;
        printf("%d\n",ret); 
    }
}
int main()
{
    Init();
    query();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值