【bzoj3489】 A simple rmq problem k-d树

由于某些原因,我先打了一个错误的树套树,后来打起了$k-d$。接着因不明原因在思路上被卡了很久,在今天中午蹲坑时恍然大悟......

对于一个数字$a_i$,我们可以用一组三维坐标$(i,pre,nxt)$来表示,其中$i$表示该数字下标,$pre$表示在区间$[1,i)$中满足$a[j]=a[i]$的最大$j$,若不存在,则$pre=0$。$nxt$表示在区间$(i,n]$中满足$a[j]=a[i]$的最小$j$,若不存在,则$nxt=n+1$。

接着我们种一棵3-d树去存储这n个点。对于任意一个节点,需存储该节点的数字,以该节点为根的字数中最大的数字,每一维的最小值和最大值。对于一组询问$[l,r]$,答案即为满足第一维在区间$[l,r]$,第二维在区间$[0,l)$,第三维在区间$(r,n+1)$中的所有点上的最大数字。在查询时,直接按照$k-d$的正常查询策略更新答案即可。

时间复杂度:$O(n log n+n^\frac{5}{3})$

警告:若采用此方法,此题建议各位加一个微小的剪枝:假定当前所得到的最大答案为$ans$,若当前访问的字树中最大答案$≤ans$,直接跳过这棵字树即可。(借助该方法极限数据耗时从17.5s降低至1.4s)

 1 #include<bits/stdc++.h>
 2 #define M 210000
 3 using namespace std;
 4 int n,m,D,root;
 5 struct kd{
 6     int a[3],max[3],min[3],now,ans,l,r;
 7     kd(){a[0]=a[1]=a[2]=now=l=r=0;min[1]=min[2]=min[0]=12345678;}
 8     kd(int xx,int yy,int zz,int kk){a[0]=xx;a[1]=yy;a[2]=zz;now=kk;}
 9     friend bool operator <(kd a,kd b){
10         return a.a[D]<b.a[D];
11     }
12 }a[M];
13 void upd(kd &x,kd &y){
14     for(int i=0;i<3;i++)
15         x.max[i]=max(x.max[i],y.max[i]),
16         x.min[i]=min(x.min[i],y.min[i]);
17 }
18 inline int nx(int d){if(d==2) return 0; return d+1;}
19 void build(int &x,int l,int r,int d){
20     if(l>r) return; int mid=(l+r)>>1;
21     D=d; nth_element(a+l,a+mid,a+r+1); x=mid;
22     for(int i=0;i<3;i++) a[x].max[i]=a[x].min[i]=a[x].a[i];
23     build(a[x].l,l,mid-1,nx(d));
24     build(a[x].r,mid+1,r,nx(d));
25     upd(a[x],a[a[x].l]); upd(a[x],a[a[x].r]);
26     a[x].ans=max(a[x].now,max(a[a[x].l].ans,a[a[x].r].ans));
27 }
28 int ans=0;
29 void query(int x,int l,int r){
30     if(x==0||ans>=a[x].ans||r<a[x].min[0]||a[x].max[0]<l||l-1<a[x].min[1]||a[x].max[2]<r+1) return;
31     if(l<=a[x].min[0]&&a[x].max[0]<=r&&a[x].max[1]<l&&r<a[x].min[2])
32     {ans=max(ans,a[x].ans); return;}
33     if(l<=a[x].a[0]&&a[x].a[0]<=r&&a[x].a[1]<l&&r<a[x].a[2]) ans=max(ans,a[x].now);
34     query(a[x].l,l,r); query(a[x].r,l,r);
35 }
36 int last[M]={0};
37 int main(){
38     scanf("%d%d",&n,&m);
39     for(int i=1;i<=n;i++){
40         int x; scanf("%d",&x);
41         a[i].a[0]=i; a[i].now=x;
42         a[i].a[1]=last[x];
43         a[last[x]].a[2]=i;
44         last[x]=i;
45     }
46     for(int i=1;i<=n;i++) if(a[i].a[2]==0) a[i].a[2]=n+1;
47     build(root,1,n,0);
48     while(m--){
49         int x,y,l,r; scanf("%d%d",&x,&y);
50         l=(x+ans)%n+1; r=(y+ans)%n+1; if(l>r) swap(l,r);
51         ans=0; query(root,l,r);
52         printf("%d\n",ans);
53     }
54 }    
时间: 2024-07-31 23:52:14

【bzoj3489】 A simple rmq problem k-d树的相关文章

bzoj3489: A simple rmq problem (主席树)

//========================== 蒟蒻Macaulish:http://www.cnblogs.com/Macaulish/  转载要声明! //========================== 说好的“因为是OJ上的题,就简单点好了.”呢? 一开始看不懂,不会写. 然后跪了一个晚上决定看云的题解&……似乎是主席树套主席树!吓傻,还开了40000000的数组.然后一交tle…… 然后p是不可能玩常数的. 找不到其他做法. 然后找到了神牛dwjshift,好心地提供了题

bzoj3489 A simple rmq problem 可持久化树套树

先预处理出两个个数组pre,next.pre[i]表示上一个与i位置数字相同的位置,若不存在则设为0:next[i]表示下一个与i位置数字相同的位置,若不存在则设为n+1.那么一个满足在区间[L,R]中只出现一次的数字,其pre[i]<L,next[i]>R. 这样我们可以先将pre进行排序,然后将pre可持久化,外层线段树套的是当前数字的位置i,内层线段树套的是next[i].外层线段树的节点总数是nlogn,内层线段树节点总数是nlogn^2.时间复杂度O(nlogn^2). 代码 1 #

[bzoj3489]A simple rmq problem

本题既不是rmq也不会simple(对我这种蒟蒻而言) 一开始只能想到树套树套树TAT然后看了看数据范围果断滚去膜拜题解. 然后才知道预先排序一下可以弄掉一个log.不过得写可持久化线段树套可持久化线段树.. 然后愉悦的开码了...感人的是竟然不用调...更感人的是交上去直接tle了. 然后从网上找了别人的代码(方法一样)发现同样的数据我要跑6s+..标称只要2s+.. 之后各种卡常还是慢了一倍TAT...最后自己写个max函数就和标程一样快了TAT这几天怎么总是出些奇怪的状况QAQ. 本来故事

【kd-tree】bzoj3489 A simple rmq problem

Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足(pre[i]<ql and nex[i]>qr and i∈[ql,qr]) 然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路不由自主又代入到搞[子树最大值]来更新答案……然而忘记了

BZOJ 3489 A simple rmq problem 可持久化树套树

题目大意:给定一个序列,多次询问某一区间中出现且仅出现一次的最大的数 令第i个数左侧第一个与这个数相同的数为last[i] 右侧第一个与这个相同的数为next[i] 那么一个数a[i]在区间内出现一次当且仅当last[i]<l&&next[i]>r&&l<=i<=r 于是我们将元素按照last[i]排序并构建可持久化线段树 令pos为满足last[i]<l的最大的i 每次查询我要查询的是第pos个版本的线段树内所有next[i]>r的数中

【BZOJ3489】A simple rmq problem kd-tree

[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行,每

【BZOJ】【3489】A simple rmq problem

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])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路

2014 Super Training #9 F A Simple Tree Problem --DFS+线段树

原题: ZOJ 3686 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3686 这题本来是一个比较水的线段树,结果一个mark坑了我好几个小时..哎.太弱. 先DFS这棵树,树形结构转换为线性结构,每个节点有一个第一次遍历的时间和最后一次遍历的时间,之间的时间戳都为子树的时间戳,用线段树更新这段区间即可实现更新子树的效果,用到懒操作节省时间. 坑我的地方: update时,不能写成:tree[rt].mark = 1,

bzoj 3489 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