标签:end 复杂度 upd efi 复杂 最大数 names 节点 last
由于某些原因,我先打了一个错误的树套树,后来打起了$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 }
【bzoj3489】 A simple rmq problem k-d树
标签:end 复杂度 upd efi 复杂 最大数 names 节点 last
原文地址:http://www.cnblogs.com/xiefengze1/p/7930720.html