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 
#include 
#include 
#include 
#include 
#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



你可能感兴趣的:(CodeForces,线段树/平衡树)