bzoj 3489: A simple rmq problem (KD-tree)

3489: A simple rmq problem

Time Limit: 40 Sec   Memory Limit: 600 MB
Submit: 1590   Solved: 518
[ Submit][ Status][ Discuss]

Description

因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。

 

Input

第一行为两个整数N,MM是询问数,N是序列的长度(N<=100000M<=200000)

第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N

再下面M行,每行两个整数xy

询问区间[l,r]由下列规则产生(OIER都知道是怎样的吧>_<)

l=min(x+lastans)mod n+1,(y+lastansmod n+1);

r=max(x+lastans)mod n+1,(y+lastansmod n+1);

Lastans表示上一个询问的答案,一开始lastans0

Output

一共M行,每行给出每个询问的答案。

Sample Input

10 10
6 4 9 10 9 10 9 4 10 4
3 8
10 1
3 4
9 4
8 1
7 8
2 9
1 1
7 3
9 9

Sample Output

4
10
10
0
0
10
0
4
0
4

HINT



注意出题人为了方便,input的第二行最后多了个空格。


2015.6.24新加数据一组,2016.7.9放至40S,600M,但未重测


 

Source

[ Submit][ Status][ Discuss]

题解:KD-tree

这道题应该标算不是KD-tree,ATP大神说它写的是主席树套树,好高端,不会啊,呜呜呜。。。

于是就直接上KD-tree了。

对于每个位置,我们维护三个维度为该位置,与该位置同权值的前一个数的位置和后一个数的位置(x,y,z)

我们要找的数是所有满足

x>=l&&x<=r

y<l  z>r 的数中最大值。

那么直接用KD-tree统计答案即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200003
using namespace std;
int n,m,cmpd,root,pre[N],next[N],point[N],ans,lastans,x,y,val[N];
struct data {
    int d[3],mx[3],mn[3],val,maxn;
    int l,r;
}tr[N];
int tmp[10][10];
int cmp(data a,data b)
{
    return a.d[cmpd]<b.d[cmpd];
}
void update(int now)
{
    int l=tr[now].l; int r=tr[now].r;
    for (int i=0;i<=2;i++){
        if (l) tr[now].mx[i]=max(tr[now].mx[i],tr[l].mx[i]),
               tr[now].mn[i]=min(tr[now].mn[i],tr[l].mn[i]);
        if (r) tr[now].mx[i]=max(tr[now].mx[i],tr[r].mx[i]),
               tr[now].mn[i]=min(tr[now].mn[i],tr[r].mn[i]);
    }
    if (l) tr[now].maxn=max(tr[l].maxn,tr[now].maxn);
    if (r) tr[now].maxn=max(tr[r].maxn,tr[now].maxn);
}
int build(int l,int r,int d)
{
    d%=3;
    cmpd=d;
    int mid=(l+r)/2;
    nth_element(tr+l,tr+mid,tr+r+1,cmp);
    for (int i=0;i<=2;i++)
     tr[mid].mx[i]=tr[mid].mn[i]=tr[mid].d[i];
    //cout<<tr[mid].d[0]<<" "<<tr[mid].d[1]<<" "<<tr[mid].d[2]<<endl;;
    if (l<mid) tr[mid].l=build(l,mid-1,d+1);
    if (r>mid) tr[mid].r=build(mid+1,r,d+1);
    update(mid);
    return mid;
}
int check(int now)
{
    if (tr[now].mx[0]<x||tr[now].mn[0]>y) return 0;
    if (tr[now].mn[1]>=x) return 0;
    if (tr[now].mx[2]<=y) return 0;
    return 1;  
}
void query(int now)
{
    if (tr[now].d[0]>=x&&tr[now].d[0]<=y&&tr[now].d[1]<x&&tr[now].d[2]>y)
     ans=max(ans,tr[now].val);
    int l=tr[now].l; int r=tr[now].r;
    if (l&&tr[l].maxn>ans) 
     if (check(l)) query(l);
    if (r&&tr[r].maxn>ans)
     if (check(r)) query(r);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&val[i]);
    memset(point,0,sizeof(point));
    for (int i=1;i<=n;i++)
     pre[i]=point[val[i]],point[val[i]]=i;
    for (int i=1;i<=n;i++) point[i]=n+1;
    for (int i=n;i>=1;i--) 
     next[i]=point[val[i]],point[val[i]]=i;
    for (int i=1;i<=n;i++)
     tr[i].d[0]=i,tr[i].d[1]=pre[i],tr[i].d[2]=next[i],tr[i].val=tr[i].maxn=val[i];
    root=build(1,n,0);
    lastans=0;
    for (int i=1;i<=m;i++) {
        scanf("%d%d",&x,&y);
        x=(x+lastans)%n+1;
        y=(y+lastans)%n+1;
        if (x>y) swap(x,y);
        ans=0;
        query(root);
        printf("%d\n",ans);
        lastans=ans;
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值