把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ3489】A simple rmq problem(三维数点)

点此看题面

大致题意: 给你一个序列,每次询问一个区间只出现过一次的数中的最大值。

三维数点

考虑我们令\(pre_i,nxt_i\)表示第\(i\)个数上一个位置和下一个位置。

则第\(i\)个数能对询问\([x,y]\)造成贡献,就需要满足\(x\le i\le y,pre_i<x,nxt_i>y\)

这显然是一个三维数点问题,于是我们就可以使用\(KD-Tree\)

若一个子树中最大值小于等于当前\(ans\),或者三维中有至少一维肯定不满足条件,可以直接return。

否则若三维都肯定满足条件,就更新答案为子树中的最大值,然后return。

大致思路就是这样,具体实现可以详见代码。

KD-Tree建树时会忘记写d,结果只按一维排序的,可能也只有就我一个吧。。。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,D,a[N+5],lnk[N+5];
struct Point
{
	int v,x[3];I int& operator [] (CI d) {return x[d];}
	I bool operator < (Con Point& o) Con {return x[D]<o.x[D];}
}p[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
		#undef D
}F;
class KDTree//KD-Tree
{
	private:
		int rt,Nt,ans;struct node {int G,S[2];Point V,Mx,Mn;}O[N+5];
		I void PU(CI x)//上传信息
		{
			RI i;for(i=0;i^3;++i) O[x].Mx[i]=O[x].Mn[i]=O[x].V[i],//维护子树边界
				O[x].S[0]&&(Gmax(O[x].Mx[i],O[O[x].S[0]].Mx[i]),Gmin(O[x].Mn[i],O[O[x].S[0]].Mn[i])),
				O[x].S[1]&&(Gmax(O[x].Mx[i],O[O[x].S[1]].Mx[i]),Gmin(O[x].Mn[i],O[O[x].S[1]].Mn[i]));
			O[x].G=max(O[x].V.v,max(O[O[x].S[0]].G,O[O[x].S[1]].G));//统计子树最大值
		}
		I void Build(CI l,CI r,int& rt,Point *P,CI d=0)//建树
		{
			int mid=l+r>>1;D=d,nth_element(P+l+1,P+mid+1,P+r+1),O[rt=++Nt].V=P[mid],
			l<mid?(Build(l,mid-1,O[rt].S[0],P,(d+1)%3),0):(O[rt].S[0]=0),
			r>mid?(Build(mid+1,r,O[rt].S[1],P,(d+1)%3),0):(O[rt].S[1]=0),PU(rt);
		}
		I void Qry(CI rt,CI x,CI y)//询问
		{
			if(!rt||O[rt].G<=ans||O[rt].Mn[0]>y||O[rt].Mx[0]<x||O[rt].Mn[1]>=x||O[rt].Mx[2]<=y) return;//若肯定无法造成贡献
			if(O[rt].Mn[0]>=x&&O[rt].Mx[0]<=y&&O[rt].Mx[1]<x&&O[rt].Mn[2]>y) return (void)(ans=O[rt].G);//若肯定符合条件
			O[rt].V[0]>=x&&O[rt].V[0]<=y&&O[rt].V[1]<x&&O[rt].V[2]>y&&Gmax(ans,O[rt].V.v);//判断当前节点贡献
			RI d=O[O[rt].S[1]].G>=O[O[rt].S[0]].G;Qry(O[rt].S[d],x,y),Qry(O[rt].S[d^1],x,y);//优先处理可能带来更优答案的子树
		}
	public:
		I void Build(Point *P) {Build(1,n,rt,P);}
		I int Qry(CI x,CI y) {return ans=0,Qry(rt,x,y),ans;}
}K;
int main()
{
	RI i,Qt,x,y,lst=0;F.read(n),F.read(Qt);
	for(i=1;i<=n;++i) F.read(p[i].v),p[i][0]=i,p[i][1]=lnk[p[i].v],lnk[p[i].v]=i;//求前驱
	for(i=1;i<=n;++i) lnk[i]=n+1;for(i=n;i;--i) p[i][2]=lnk[p[i].v],lnk[p[i].v]=i;K.Build(p);//求后继,然后建树
	W(Qt--) F.read(x),F.read(y),x=(x+lst)%n+1,//强制在线
		y=(y+lst)%n+1,x>y&&(x^=y^=x^=y),F.writeln(lst=K.Qry(x,y));
	return F.clear(),0;
}
posted @ 2020-05-19 16:29  TheLostWeak  阅读(234)  评论(0编辑  收藏  举报