BZOJ 3489: A simple rmq problem【KD树】

题目传送门

题目分析:

记下每个数的 前一次出现位置 p r e pre pre 和 后一次出现位置 n x t nxt nxt
于是每个点就变成一个三维的点 ( p o s i , p r e i , n x t i ) (pos_i,pre_i,nxt_i) (posi,prei,nxti)
那么查询 [ l , r ] [l,r] [l,r]只出现一次数的最大值
就相当于查询 p o s ∈ [ l , r ] , p r e ∈ [ 0 , l − 1 ] , n x t ∈ [ r + 1 , n + 1 ] pos\in[l,r],pre\in[0,l-1],nxt\in[r+1,n+1] pos[l,r]pre[0,l1]nxt[r+1,n+1]的点的最大值
裸裸的三维KD树就OK了(什么二维线段树不存在的

Code(10188 ms):

#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 100005
#define lc t[o].ch[0]
#define rc t[o].ch[1]
using namespace std;
inline void read(int &a){
	char c;while(!isdigit(c=getchar()));
	for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int n,m,D,root,v[maxn],pos[maxn];
int ql,qr,ans;
struct KD_tree{
	int d[3],mn[3],mx[3],ch[2],v,s;
	bool operator < (const KD_tree &t)const{return d[D]<t.d[D];}
}t[maxn];
inline void cmin(int &a,int b){if(a>b) a=b;}
inline void cmax(int &a,int b){if(a<b) a=b;}
inline void mt(int o,int x){
	t[o].s=max(t[o].s,t[x].s);
	for(int i=0;i<3;i++) cmin(t[o].mn[i],t[x].mn[i]),cmax(t[o].mx[i],t[x].mx[i]);
}
inline void mt(int o){if(lc) mt(o,lc); if(rc) mt(o,rc);}
int build(int l,int r,int nd){
	int o=(l+r)>>1;D=nd;
	nth_element(t+l,t+o,t+r+1);
	t[o].s=t[o].v;
	for(int i=0;i<3;i++) t[o].mn[i]=t[o].mx[i]=t[o].d[i];
	if(l<o) lc=build(l,o-1,(nd+1)%3); else lc=0;
	if(r>o) rc=build(o+1,r,(nd+1)%3); else rc=0;
	mt(o);return o;
}
inline bool check(int o){
	if(!o) return 0;
	if(t[o].mx[0]<ql||t[o].mn[0]>qr) return 0;
	if(t[o].mn[1]>=ql||t[o].mx[2]<=qr) return 0;
	return 1;
}
void query(int o)
{
	if(ql<=t[o].mn[0]&&t[o].mx[0]<=qr&&t[o].mx[1]<ql&&t[o].mn[2]>qr) {ans=max(ans,t[o].s);return;}
	if(ql<=t[o].d[0]&&t[o].d[0]<=qr&&t[o].d[1]<ql&&t[o].d[2]>qr) ans=max(ans,t[o].v);
	int w[2]={t[lc].s,t[rc].s},tmp=w[1]>=w[0];
	if(ans<w[tmp]&&check(t[o].ch[tmp])) query(t[o].ch[tmp]);tmp^=1;
	if(ans<w[tmp]&&check(t[o].ch[tmp])) query(t[o].ch[tmp]);
}
int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++){
		read(v[i]);
		t[i].v=v[i];
		t[i].d[0]=i,t[i].d[1]=pos[v[i]];
		t[i].d[2]=n+1,t[pos[v[i]]].d[2]=i;
		pos[v[i]]=i;
	}
	root=build(1,n,0);
	int x,y,l,r;
	while(m--){
		read(x),read(y);
		l=min((x+ans)%n+1,(y+ans)%n+1);
		r=max((x+ans)%n+1,(y+ans)%n+1);
		ql=l,qr=r,ans=0;query(root);
		printf("%d\n",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值