zoukankan      html  css  js  c++  java
  • 【BZOJ3489】A simple rmq problem(三维数点)

    点此看题面

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

    三维数点

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

    则第(i)个数能对询问([x,y])造成贡献,就需要满足(xle ile 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('
    ');}
    		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;
    }
    
  • 相关阅读:
    正则表达式
    linux中的三种时间
    用户,用户组
    inode与block
    linux文件属性
    linux的启动过程
    linux的重要子目录
    mail邮箱
    spark原理
    spark部署
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3489.html
Copyright © 2011-2022 走看看