您的位置:首页 > 其它

【BZOJ】【3489】A simple rmq problem

2015-05-22 17:36 323 查看

KD-Tree(乱搞)

  Orz zyf教给蒟蒻做法

  蒟蒻并不会这题正解……(可持久化树套树?。。。Orz

  对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足$ ( pre[i]<ql \ and \ nex[i]>qr\ and\ i \in [ql,qr] ) $

  然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值!

这题我的感受:

因为前面做了两道区域求和的……然后思路不由自主又代入到搞【子树最大值】来更新答案……然而忘记了单点更新,也就是:虽然这个子树不合法,但是这一个点(根)还是可能合法的……

然后就是:KD-Tree如果可以搞整个子树的话,那么用整个子树的最值去更新,会优化很多……?

终于1A了一道KD-Tree啦~好开心(虽然不是自己想出的做法……)

一个更加优秀的做法:/article/6551937.html

/**************************************************************
Problem: 3489
User: Tunix
Language: C++
Result: Accepted
Time:3712 ms
Memory:7920 kb
****************************************************************/

//BZOJ 3489
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define rep(i,n) for(int i=0;i<n;++i)
#define F(i,j,n) for(int i=j;i<=n;++i)
#define D(i,j,n) for(int i=j;i>=n;--i)
#define pb push_back
using namespace std;
typedef long long LL;
inline int getint(){
int r=1,v=0; char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1;
for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch;
return r*v;
}
const int N=100010,INF=1e9;
/*******************template********************/
int n,m,D,a
,root,ans,now
,nex
,pre
;
struct node{
int d[3],mn[3],mx[3],l,r,v,vmax;
int& operator [] (int i) {return d[i];}
}t
;
//d[0] 下标
//d[1] nex
//d[2] pre
bool operator < (node a,node b){return a[D]<b[D];}

#define L t[o].l
#define R t[o].r
#define mid (l+r>>1)
void Push_up(int o){
F(i,0,2){
t[o].mn[i]=min(t[o][i],min(t[L].mn[i],t[R].mn[i]));
t[o].mx[i]=max(t[o][i],max(t[L].mx[i],t[R].mx[i]));
}
t[o].vmax=max(t[o].v,max(t[L].vmax,t[R].vmax));
}

int build(int l,int r,int dir){
D=dir;
nth_element(t+l,t+mid,t+r+1);
int o=mid;
L = l < mid ? build(l,mid-1,(dir+1)%3) : 0;
R = mid < r ? build(mid+1,r,(dir+1)%3) : 0;
Push_up(o);
return o;
}
int ql,qr;
inline bool check(int o){
if (!o) return 0;
if (t[o].mx[1]<=qr || t[o].mn[2]>=ql) return 0;
if (t[o].mn[0]>qr || t[o].mx[0]<ql) return 0;
return 1;
}
void query(int o){
if (!o) return;
if (t[o].mn[0]>=ql && t[o].mx[0]<=qr && t[o].mn[1]>qr && t[o].mx[2]<ql){
ans=max(ans,t[o].vmax);
return;
}
if (t[o][0]>=ql && t[o][0]<=qr && t[o][1]>qr && t[o][2]<ql)
ans=max(ans,t[o].v);
if (t[L].vmax>t[R].vmax){
if (t[L].vmax>ans && check(L)) query(L);
if (t[R].vmax>ans && check(R)) query(R);
}else{
if (t[R].vmax>ans && check(R)) query(R);
if (t[L].vmax>ans && check(L)) query(L);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("3489.in","r",stdin);
freopen("3489.out","w",stdout);
#endif
F(i,0,2) t[0].mn[i]=INF,t[0].mx[i]=-INF;
t[0].vmax=-1;
n=getint(); m=getint();
F(i,1,n) a[i]=getint();
F(i,1,n){
pre[i]=now[a[i]];
now[a[i]]=i;
}
F(i,1,n) nex[pre[i]]=i;
F(i,1,n) if (!nex[i]) nex[i]=n+1;
F(i,1,n) t[i][0]=i,t[i][1]=nex[i],t[i][2]=pre[i],t[i].v=a[i];
root=build(1,n,0);
F(i,1,m){
int x=getint(),y=getint();
ql=min( (x+ans)%n+1,(y+ans)%n+1);
qr=max( (x+ans)%n+1,(y+ans)%n+1);
ans=0;
query(root);
printf("%d\n",ans);
}
return 0;
}


View Code

3489: A simple rmq problem

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 554 Solved: 173
[Submit][Status][Discuss]

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的第二行最后多了个空格。

Source

by zhzqkkk

[Submit][Status][Discuss]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: