[bzoj3489][kdtree]A simple rmq problem

6 篇文章 0 订阅

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,但未重测

题解

发现kdt原来这么强大
原来想了好久可持久化线段树没想到,然后发现做不到。
然后就%了一发发现,原来还有这种操作。
设第i个元素前一个相同元素的位置是pre[i],后一个相同元素的位置是nex[i],元素值为sum[i]
对于l~r,其实就是要找一个l<=i<=r,其中sum[i]最大且pre[i]< l && nex[i]>r
建立三元组(i,pre[i],nex[i]),值即为sum[i]
那么其实就是搞一个三维空间了,三维空间内确定一个范围并找到这个范围内的最大值
切割多维空间就是kdtree啦
在kdtree里进行贪心查找并剪枝即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node
{
    int d[3],mn[3],mx[3],lc,rc;//0 位置 1 pre 2 nex 
    int s,id,pre,nex,smax;
}tr[110000],root;
void upd(int now)
{
    int lc=tr[now].lc,rc=tr[now].rc;
    if(lc){for(int i=0;i<3;i++)tr[now].mx[i]=max(tr[now].mx[i],tr[lc].mx[i]),tr[now].mn[i]=min(tr[now].mn[i],tr[lc].mn[i]);tr[now].smax=max(tr[now].smax,tr[lc].smax);}
    if(rc){for(int i=0;i<3;i++)tr[now].mx[i]=max(tr[now].mx[i],tr[rc].mx[i]),tr[now].mn[i]=min(tr[now].mn[i],tr[rc].mn[i]);tr[now].smax=max(tr[now].smax,tr[rc].smax);}
}
int cmpd;
bool cmp(node n1,node n2){return n1.d[cmpd]<n2.d[cmpd];}
int bt(int l,int r,int d)
{
    int mid=(l+r)/2;cmpd=d;
    int now=mid;
    nth_element(tr+l,tr+mid,tr+r+1,cmp);
    tr[now].smax=tr[now].s;
    if(l<mid)tr[now].lc=bt(l,mid-1,(d+1)%3);
    if(mid<r)tr[now].rc=bt(mid+1,r,(d+1)%3);
    upd(now);
    return now;
}
int ql,qr,ans;
bool chk(int now)
{
    if(now==0)return false;
    if(tr[now].mx[0]<ql || tr[now].mn[0]>qr)return false;
    if(tr[now].mn[1]>=ql || tr[now].mx[2]<=qr)return false;
    return true;
}
void fd(int now)
{
    if(tr[now].mn[0]>=ql && tr[now].mx[0]<=qr && tr[now].mx[1]<ql && tr[now].mn[2]>qr)
    {
        ans=max(ans,tr[now].smax);
        return ;
    }
    if(tr[now].id>=ql && tr[now].id<=qr && tr[now].pre<ql && tr[now].nex>qr)ans=max(ans,tr[now].s);
    int sx=tr[tr[now].lc].smax,sy=tr[tr[now].rc].smax;
    if(sx>sy)
    {
        if(tr[now].lc && sx>ans && chk(tr[now].lc))fd(tr[now].lc);
        if(tr[now].rc && sy>ans && chk(tr[now].rc))fd(tr[now].rc);
    }
    else
    {
        if(tr[now].rc && sy>ans && chk(tr[now].rc))fd(tr[now].rc);
        if(tr[now].lc && sx>ans && chk(tr[now].lc))fd(tr[now].lc);
    }
}
int n,m,a[110000];
int P[110000],N[110000],lastans;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
    {
        tr[i].pre=tr[i].mn[1]=tr[i].mx[1]=tr[i].d[1]=P[a[i]];
        P[a[i]]=i;
    }
    memset(N,63,sizeof(N));
    for(int i=n;i>=1;i--)
    {
        tr[i].nex=tr[i].mn[2]=tr[i].mx[2]=tr[i].d[2]=N[a[i]];
        N[a[i]]=i;
    }
    for(int i=1;i<=n;i++)tr[i].s=a[i],tr[i].id=tr[i].d[0]=tr[i].mx[0]=tr[i].mn[0]=i;
    int root=bt(1,n,0);
    lastans=0;
    while(m--)
    {
        int x=read(),y=read();
        ql=min((x+lastans)%n+1,(y+lastans)%n+1);
        qr=max((x+lastans)%n+1,(y+lastans)%n+1);
        ans=0;fd(root);
        printf("%d\n",ans);lastans=ans;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值