您的位置:首页 > 其它

BZOJ3489 A simple rmq problem

2016-06-17 22:02 357 查看

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新加数据一组,但未重测

Source

by zhzqkkk

正解:kd-tree

解题报告:

  做这道题本来是为了试试可持久化线段树的,然而感觉好复杂。结果在网上突然看到一份博客,居然用的是kd-tree,当时就激动了。

  参考博客:http://trinklee.blog.163.com/blog/static/2381580602015422933539/

  这份博客讲的很清楚了,三维kd-tree。我们维护三个值,当前值的上一次出现位置,下一次出现位置。显然查询区间[l,r]时i满足当且仅当 last[num[i]]<l && next[num[i]]>r 时才需被计入贡献,满足要求。

  那么这样的查询显然是可以用kd-tree来做的。都是老套路了,想好怎么建了就跟我之前做的两道kd-tree题一样了。还没有插入操作了,还简单些了好吗

  代码如下:

//It is made by jump~
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#ifdef WIN32
#define OT "%I64d"
#else
#define OT "%lld"
#endif
using namespace std;
typedef long long LL;
const int MAXN = 100011;
int shu[MAXN];
int n,m;
int last[MAXN];
int L[MAXN],R[MAXN];
int root;
int D;
int ans;
int cha[3][2];

struct node{
int l,r;
int Min[3],Max[3],d[3];
int num;
int zhi;
}a[MAXN];

inline int getint()
{
int w=0,q=0;
char c=getchar();
while((c<'0' || c>'9') && c!='-') c=getchar();
if (c=='-')  q=1, c=getchar();
while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
return q ? -w : w;
}

inline bool cmp(node q,node qq){
for(int i=0;i<=2;i++)
if(q.d[(D+i)%3]!=qq.d[(D+i)%3]) return (q.d[(D+i)%3]<qq.d[(i+D)%3]);
return 1;
}

inline void update(int x){
if(a[x].l) {
for(int i=0;i<=2;i++) a[x].Min[i]=min(a[x].Min[i],a[a[x].l].Min[i]),a[x].Max[i]=max(a[x].Max[i],a[a[x].l].Max[i]);
a[x].zhi=max(a[x].zhi,a[a[x].l].zhi);
}
if(a[x].r) {
for(int i=0;i<=2;i++) a[x].Min[i]=min(a[x].Min[i],a[a[x].r].Min[i]),a[x].Max[i]=max(a[x].Max[i],a[a[x].r].Max[i]);
a[x].zhi=max(a[x].zhi,a[a[x].r].zhi);
}
}

inline int build_tree(int l,int r,int d){
int mid=(l+r)/2; D=d;
nth_element(a+l+1,a+mid+1,a+r+1,cmp);
a[mid].zhi=a[mid].num;
for(int i=0;i<=2;i++) a[mid].Min[i]=a[mid].Max[i]=a[mid].d[i];

if(l!=mid) a[mid].l=build_tree(l,mid-1,(d+1)%3);
if(mid!=r) a[mid].r=build_tree(mid+1,r,(d+1)%3);
update(mid);
return mid;
}

inline bool pan(int now){
for(int i=0;i<=2;i++) {
if(a[now].Max[i]<cha[i][0] || a[now].Min[i]>cha[i][1]) return false;
}
return true;
}

inline void query(int now) {
if(a[now].zhi<ans) return ;//剪枝
bool flag=true;
for(int i=0;i<=2;i++) if(cha[i][0]>a[now].Min[i] || cha[i][1]<a[now].Max[i] ) { flag=false; break;}
if(flag) { ans=max(ans,a[now].zhi); return ; }
flag=true;

for(int i=0;i<=2;i++) if(cha[i][0]>a[now].d[i] || cha[i][1]<a[now].d[i]) flag=false;
if(flag) { ans=max(ans,a[now].num); }
if(a[now].l && pan(a[now].l)) { query(a[now].l); }
if(a[now].r && pan(a[now].r)) { query(a[now].r); }
}

inline void solve(){
n=getint(); m=getint();

for(int i=1;i<=n;i++) {
shu[i]=getint();
L[i]=last[shu[i]];
last[shu[i]]=i;
}

for(int i=1;i<=n;i++) last[i]=n+1;

for(int i=n;i>=1;i--) R[i]=last[shu[i]],last[shu[i]]=i;

for(int i=1;i<=n;i++) {
a[i].d[0]=i; a[i].d[1]=L[i]; a[i].d[2]=R[i];
a[i].num=shu[i];
}

root=build_tree(1,n,0);

int l,r,x,y;
for(int i=1;i<=m;i++) {
l=getint(); r=getint();
x=(l+ans)%n+1; y=(r+ans)%n+1;
if(x<y) l=x,r=y;
else l=y,r=x;

ans=0;
cha[0][0]=l; cha[0][1]=r;
cha[1][0]=0; cha[1][1]=l-1;
cha[2][0]=r+1; cha[2][1]=n+1;
query(root);
printf("%d\n",ans);
}
}

int main()
{
solve();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: