bzoj3489 A simple rmq problem

http://www.elijahqi.win/2018/01/12/bzoj3489-a-simple-rmq-problem/
Description

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

Input

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

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

再下面M行,每行两个整数x,y,

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

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

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

Lastans表示上一个询问的答案,一开始lastans为0
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,但未重测

kd-tree可以暴力搞过 没有被卡死

题意:每次询问一个区间 然后求出这个区间内最大的那个数 据说标算是树套树什么的?

直接来做似乎不太好做 我们呢考虑一个数如何对某些区间产生影响 首先我们针对每个数求出他前一次数出现的位置 然后再求出他后一次出现的位置这个怎么求 根据zhx巨佬的方法 我可以o(n)小常数的搞出来记录b[]表示下标数值这个数上一次出现的位置 每次我上一个数出现的下一次就是我现在这个数 我这个数 上一次出现的位置就是b[] 然后就求出来了 接下来 我们将这个内容转成(i,j,k)一个三维空间的坐标第一维是 我当前这个数处在第几维度 j表示我这个数前一次出现是在哪里 k表示我这个数后一次出现是在哪里 针对一个询问区间L,R我们可以得知 我相当于需要求一个立方体内点权值的最大 那么kd-tree就可以搞了 我知道范围是L<=i<=R 并且j< L&k>R 这种情况的点才满足


#include<cstdio>
#include<algorithm>
#define N 110000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=gc();
    return x;
}
int D,L,R,nxt[N],b[N],pre[N],a[N],n,m,ans,root;
struct node1{
    int d[3],v;
    int& operator[](int x) {return d[x];}
    friend bool operator<(node1 a,node1 b){return a[D]<b[D];}
}point[N];
struct node{
    node1 x;int left,right,min[3],max[3],maxv;
}tree[N];
inline void update(int x){
    int l=tree[x].left,r=tree[x].right;
    tree[x].maxv=max(tree[x].maxv,max(tree[l].maxv,tree[r].maxv));
    for (int i=0;i<3;++i) tree[x].min[i]=min(tree[x].min[i],min(tree[l].min[i],tree[r].min[i]));
    for (int i=0;i<3;++i) tree[x].max[i]=max(tree[x].max[i],max(tree[l].max[i],tree[r].max[i]));
}
inline void build(int &x,int l,int r,int dim){
    D=dim;int mid=l+r>>1;x=mid;nth_element(point+l,point+mid,point+r+1);tree[x].maxv=point[mid].v;
    tree[x].x=point[mid];for (int i=0;i<3;++i) tree[x].min[i]=tree[x].max[i]=point[mid][i];
    if (l<mid) build(tree[x].left,l,mid-1,(dim+1)%3);
    if (r>mid) build(tree[x].right,mid+1,r,(dim+1)%3);update(x);
}
inline bool judge(node1 a){
    if (a[0]<L||a[0]>R) return 0;
    if (a[1]>=L) return 0;if (a[2]<=R) return 0;
    return 1;
}
inline bool judge1(int x){
    if (tree[x].min[0]>R||tree[x].max[0]<L) return 0;
    if (tree[x].min[1]>=L) return 0;if (tree[x].max[2]<=R) return 0;
    return 1;
}
inline void query(int x){
    if (judge(tree[x].x)) if (tree[x].x.v>ans) ans=tree[x].x.v;int ansl=0,ansr=0;
    if (tree[x].left) if (judge1(tree[x].left)) ansl=tree[tree[x].left].maxv;
    if (tree[x].right) if (judge1(tree[x].right)) ansr=tree[tree[x].right].maxv;
    if (ansl>ansr){
        if (ansl>ans) query(tree[x].left);
        if (ansr>ans) query(tree[x].right);
    } else{
        if (ansr>ans) query(tree[x].right);
        if (ansl>ans) query(tree[x].left);
    }
}
int main(){
    freopen("bzoj3489.in","r",stdin);
    n=read();m=read();for (int i=0;i<3;++i) tree[0].min[i]=inf;
    for (int i=1;i<=n;++i) {
        nxt[i]=n+1;a[i]=read();nxt[b[a[i]]]=i;pre[i]=b[a[i]];b[a[i]]=i;
    }
//  for (int i=1;i<=n;++i) printf("%d %d %d\n",a[i],pre[i],nxt[i]);
    for (int i=1;i<=n;++i){point[i][0]=i;point[i][1]=pre[i];point[i][2]=nxt[i];point[i].v=a[i];}
    build(root,1,n,0);int last_ans=0;
    for (int i=1;i<=m;++i){
        int x=read(),y=read();
        L=min((x+last_ans)%n+1,(y+last_ans)%n+1);
        R=max((x+last_ans)%n+1,(y+last_ans)%n+1);
        ans=0;query(root);last_ans=ans;printf("%d\n",ans);
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值