Codeforces 12D.Ball (非递归线段树+离散化)

题意:

给N (N<=500000)个点,每个点有x,y,z ( 0<= x,y,z <=10^9 )

对于某点(x,y,z),若存在一点(x1,y1,z1)使得x1 > x && y1 > y && z1 > z 则点(x,y,z)是特殊点。

问N个点中,有多少个特殊点。


思路:

大概就是,先记下所有点,从大到小排序,先按z的降序,再按y的降序,再按x的降序排序。

将x离散化,然后,按z分层,一层一层扫描。

Z表示当前层的z值。

线段树中,存的是所有 z > Z 的点,叶节点存下每个x上的最大y,然后线段树维护区间最大值。


在每层的Z搜索中:

操作一:遍历本层的点(x,y,z) ,在线段树中搜索横坐标大于x的点中,有没有纵坐标大于y的。

因为线段树中的点的z都大于Z,所以只需要搜索x和y,若存在,则这个点属于特殊点。

操作二:然后第二次遍历本层的点,将所有点加入线段树。


代码中,每层的操作二被延后到了下一层的操作一之前。

排序复杂度O(nlog2(n))

搜索复杂度O(nlog2(n))

总复杂度O(nlog2(n))

代码如下:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define maxn 500007
using namespace std;
int Rank[maxn],Rn;
void SetRank(){//[1..Rn]->[1..I]
	int I=1;
	sort(Rank+1,Rank+Rn+1);
	for(int i=2;i<=Rn;++i) if(Rank[i]^Rank[i-1]) Rank[++I]=Rank[i];
	Rn=I;
}
int GetRank(int x){
	int L=1,R=Rn,M;//[L,R] first >= x
	while(L^R){
		M=(L+R)>>1;
		if(Rank[M] < x)L=M+1;
		else R=M;
	}
	return L;
}
int Max[maxn<<2],N;
int Build(int n){//建树 
	N=1;while(N < n+2) N <<= 1; 
	memset(Max,-1,sizeof(Max));
}
void Update(int L,int x){//用 x 更新 L 点的值 
	for(int s=N+L;s;s>>=1){
		if(x > Max[s]) Max[s]=x;
		else break;
	}
}
bool Search(int L,int y){//搜索(L,Rn]有没有值大于 y 
	for(int s=N+L;s^1;s>>=1){
		if(~s&1){
			if(Max[s^1] > y) return true;
		}
	}
	return false;
}
int n;
struct Point{
	int x,y,z;
	Point(){}
	Point(int x,int y,int z):x(x),y(y),z(z){}
	bool operator <(const Point &B)const{return z>B.z||z==B.z&&y>B.y||z==B.z&&y==B.y&&x>B.x;}
}P[maxn];
int main(void)
{
	
	while(~scanf("%d",&n)){
		Rn=0;
		for(int i=0;i<n;++i){
			scanf("%d",&P[i].x);
			Rank[++Rn]=P[i].x;
		}
		for(int i=0;i<n;++i) scanf("%d",&P[i].y);
		for(int i=0;i<n;++i) scanf("%d",&P[i].z);
		//给点排序 
		sort(P,P+n);
		//离散化 
		SetRank();
		//建树 
		Build(Rn);
		int ANS=0;
		int Z,Zi=0;
		int I=0;
		while(I<n){//扫描 
			for(int i=Zi;i<I;++i){//更新线段树 
				Update(GetRank(P[i].x),P[i].y);
			}
			Z=P[I].z;//记录本层Z值 
			Zi=I;//记录本层起点,方便更新线段树 
			//计算 
			while(I < n && P[I].z==Z){ 
				ANS+=Search(GetRank(P[I].x),P[I].y);
				++I;
			}
		}
		printf("%d\n",ANS);
	}
return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值