【BZOJ3489】A simple rmq problem(kd-tree)

先考虑怎么样才能保证某个数只出现了一次。

对于序列中的某个数 a [ i ] a[i] a[i],我们求出上一个出现 a [ i ] a[i] a[i] 的位置为 p r e [ i ] pre[i] pre[i],下一个出现 a [ i ] a[i] a[i] 的位置为 n x t [ i ] nxt[i] nxt[i]

那么当询问的 l l l r r r 满足 p r e [ i ] < l ⩽ i pre[i]<l\leqslant i pre[i]<li i ⩽ r < n x t [ i ] i\leqslant r<nxt[i] ir<nxt[i] 时,就会有 a [ i ] a[i] a[i] 这个数在区间 [ l , r ] [l,r] [l,r] 内只出现过一次。

所以我们对于每一个 i i i,把 ( i , p r e [ i ] , n x t [ i ] ) (i,pre[i],nxt[i]) (i,pre[i],nxt[i]) 当作一个三维坐标,用 k d − t r e e kd-tree kdtree 维护即可。

T L E TLE TLE 了怎么办?

  1. P o i n t Point Point 从结构体里拉出来。

  2. c m p cmp cmp 按我的写法改一下

  3. b u i l d build build 按我的写法改一下(直接把 m i d mid mid 当作当前点的编号)

  4. q u e r y query query 时判断一下先去左儿子还是右儿子。

希望对你有帮助。

代码和注释如下:

#include<bits/stdc++.h>

#define N 200010

using namespace std;

int D;

struct Point
{
	int num[3],val;
	Point(){};
	Point(const int x,const int y,const int z,const int vall)
	{
		num[0]=x,num[1]=y,num[2]=z,val=vall;
	}
	bool operator < (const Point &b) const
	{
		if(num[D]==b.num[D])
		{
			if(num[(D+1)%3]==b.num[(D+1)%3]) return num[(D+2)%3]<b.num[(D+2)%3];
			return num[(D+1)%3]<b.num[(D+1)%3];
		}
		return num[D]<b.num[D];
	}
}p[N];

struct Tree
{
	int ch[2],maxn[3],minn[3],maxval;
	Point x;
}t[N];

int n,m,a[N];
int root,node;
int last[N],pre[N],nxt[N];
int l,r,ans;

void up(const int u)
{
	for(register int i=0;i<3;i++)
	{
		t[u].maxn[i]=t[u].minn[i]=t[u].x.num[i];
		if(t[u].ch[0])
		{
			t[u].maxn[i]=max(t[u].maxn[i],t[t[u].ch[0]].maxn[i]);
			t[u].minn[i]=min(t[u].minn[i],t[t[u].ch[0]].minn[i]);
		}
		if(t[u].ch[1])
		{
			t[u].maxn[i]=max(t[u].maxn[i],t[t[u].ch[1]].maxn[i]);
			t[u].minn[i]=min(t[u].minn[i],t[t[u].ch[1]].minn[i]);
		}
	}
	t[u].maxval=max(t[u].x.val,max(t[t[u].ch[0]].maxval,t[t[u].ch[1]].maxval));
}

void build(int &mid,const int l,const int r,const int d)
{
	if(l>r) return;
	mid=(l+r)>>1;
	D=d,nth_element(p+l,p+mid,p+r+1);
	t[mid].x=p[mid];
	build(t[mid].ch[0],l,mid-1,(d+1)%3);
	build(t[mid].ch[1],mid+1,r,(d+1)%3);
	up(mid);
}

void query(const int u)
{
	if(!u) return;
	if(l<t[u].minn[1]||l>t[u].maxn[0]||r<t[u].minn[0]||r>t[u].maxn[2]||t[u].maxval<ans) return;
	if(l>=t[u].maxn[1]&&l<=t[u].minn[0]&&r>=t[u].maxn[0]&&r<=t[u].minn[2])
	{
		ans=max(ans,t[u].maxval);
		return;
	}
	if(l>=t[u].x.num[1]&&l<=t[u].x.num[0]&&r>=t[u].x.num[0]&&r<=t[u].x.num[2]) ans=max(ans,t[u].x.val);
	query(t[u].ch[0]);
	query(t[u].ch[1]);
}

inline void read(int &x)
{
	x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	x*=f;
}

int main()
{
	read(n),read(m);
	for(register int i=1;i<=n;i++)
	{
		read(a[i]);
		pre[i]=last[a[i]]+1;
		nxt[last[a[i]]]=i-1;
		last[a[i]]=i;
	}
	for(register int i=1;i<=n;i++)
		p[i]=Point(i,pre[i],nxt[i]?nxt[i]:n,a[i]);
	build(root,1,n,0);
	while(m--)
	{
		read(l),read(r);
		l=(l+ans)%n+1,r=(r+ans)%n+1;
		ans=0;
		if(l>r) swap(l,r);
		query(root);
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值