KD_tree 板子

14 篇文章 0 订阅
5 篇文章 0 订阅

例题:3489: A simple rmq problem

在[l,r]内找只出现一次的,最大的数
用pre记录每个位置的数上次出现位置,nex记录下次出现位置
一个位置i的值在[l,r]内只出现一次,那么 l <= i <= r , prei< l, nexi>r,所以给一个点三维坐标(i,prei,nexi),询问时用kd-tree找(l~r,0~l-1,r+1~n+1)里最大的值

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

int read()
{
    char c; int x;
    while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
    return x;
}
void up(int &x,const int y){if(x<y)x=y;}
void down(int &x,const int y){if(x>y)x=y;}

const int maxn = 110000;
const int maxd = 3;
struct Kd_tree
{
    int s,c,pos[maxd],u[maxd],d[maxd],lc,rc;
}kd[maxn]; int root;
int n,m;
int cmp_d;
bool cmp(const Kd_tree &x,const Kd_tree &y) { return x.pos[cmp_d]<y.pos[cmp_d]; }

void push_up(const int &x)
{
    const int lc=kd[x].lc,rc=kd[x].rc;
    if(lc)
    {
        for(int i=0;i<maxd;i++)
            up(kd[x].u[i],kd[lc].u[i]),
            down(kd[x].d[i],kd[lc].d[i]);
        up(kd[x].c,kd[lc].c);
    }
    if(rc)
    {
        for(int i=0;i<maxd;i++)
            up(kd[x].u[i],kd[rc].u[i]),
            down(kd[x].d[i],kd[rc].d[i]);
        up(kd[x].c,kd[rc].c);
    }
}
int Build(int l,int r,int D)
{
    cmp_d=D; int mid=(l+r)>>1;
    std::nth_element(kd+l,kd+mid,kd+r+1,cmp);
    for(int i=0;i<maxd;i++)
        kd[mid].u[i]=kd[mid].d[i]=kd[mid].pos[i];
    kd[mid].c=kd[mid].s; kd[mid].lc=kd[mid].rc=0;

    if(l==r) return mid;
    if(l!=mid) kd[mid].lc=Build(l,mid-1,(D+1)%3);
    if(mid!=r) kd[mid].rc=Build(mid+1,r,(D+1)%3);
    push_up(mid);
    return mid;
}
int Pu[maxd],Pd[maxn];
int Cal(const int x)
{
    if(!x) return 0;
    for(int i=0;i<maxd;i++)
        if(kd[x].d[i]>Pu[i]||kd[x].u[i]<Pd[i]) return 0;
    return kd[x].c;
}
int Ask(const int x)
{
    for(int i=0;i<maxd;i++)
        if(kd[x].d[i]>Pu[i]||kd[x].u[i]<Pd[i]) return 0;

    bool flag=true;
    for(int i=0;i<maxd;i++)
        if(!(Pd[i]<=kd[x].d[i]&&kd[x].u[i]<=Pu[i])) {flag=false; break;}
    if(flag) return kd[x].c;

    int re=0;
    flag=true;
    for(int i=0;i<maxd;i++)
        if(!(Pd[i]<=kd[x].pos[i]&&kd[x].pos[i]<=Pu[i])) {flag=false; break;}
    if(flag) re=kd[x].s;

    int k1=Cal(kd[x].lc),k2=Cal(kd[x].rc);
    if(k1>k2)
    {
        if(k1>re) up(re,Ask(kd[x].lc));
        if(k2>re) up(re,Ask(kd[x].rc));
    }
    else
    {
        if(k2>re) up(re,Ask(kd[x].rc));
        if(k1>re) up(re,Ask(kd[x].lc));
    }
    return re;
}
int Posi[maxn];
int a[maxn],pre[maxn],nex[maxn];

int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++)
        pre[i]=Posi[a[i]],Posi[a[i]]=i;
    for(int i=1;i<=n;i++) Posi[i]=n+1;
    for(int i=n;i>=1;i--)
        nex[i]=Posi[a[i]],Posi[a[i]]=i;

    for(int i=1;i<=n;i++)
        kd[i].s=a[i],kd[i].pos[0]=i,kd[i].pos[1]=pre[i],kd[i].pos[2]=nex[i];
    Build(1,n,0); root=(1+n)>>1;
    int ans=0;
    while(m--)
    {
        int x=(read()+ans)%n+1,y=(read()+ans)%n+1;
        int l=min(x,y),r=max(x,y);
        Pd[0]=l,Pu[0]=r;
        Pd[1]=0,Pu[1]=l-1;
        Pd[2]=r+1,Pu[2]=n+1;
        printf("%d\n",ans=Ask(root));
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值