BZOJ2957 楼房重建(线段树)

【题解】

题目描述有误,一栋楼房可见,应满足其最高点到(0,0)的连线不与其他楼房相交 
这个条件可以等价为:它的斜率比之前的任何一个都大(相等也不行)
因此,只需O(logN)修改、O(logN)求出区间[1,n]内 比之前的任何一个数都大 的数有多少个 

能使用线段树的条件是:两个子问题可以合并 
本题中,记cntv[o]为:仅考虑o所代表的区间,有多少满足条件的数 
那么首先,cntv[o]一定包含cntv[o*2]; 其次,属于cntv[o*2]的建筑有可能挡住属于cntv[o*2+1]的建筑,难点在于需求出这个数目 
记 o*2 代表的区间中最大数为M,并将 o*2+1 所代表的区间分左右两段,记左段代表的区间中最大数为M2,继续讨论:
1. 若M大于等于M2,则左段全部不符合要求,递归判断右段有多少个大于M的数 
2. 若M小于M2,则右段的答案不变,为cntv[o*2+1]-cntv[左段],递归判断左段有多少个大于M的数 


【代码】

#include<stdio.h>
#include<stdlib.h>
double maxv[400005];
int cntv[400005];
double max(double a,double b)
{
	if(a>b) return a;
	return b;
}
int count(double M,int o,int left,int right)
{
	int mid=(left+right)/2;
	if(left==right) return M<maxv[o];
	if(M>=maxv[o*2]) return count(M,o*2+1,mid+1,right);
	return cntv[o]-cntv[o*2]+count(M,o*2,left,mid);//巧妙 
}
void xg(double p,int x,int o,int left,int right)
{
	int mid=(left+right)/2;
	if(left==right)
	{
		cntv[o]=1;
		maxv[o]=p;
		return;
	}
	if(x<=mid) xg(p,x,o*2,left,mid);
	if(x>mid) xg(p,x,o*2+1,mid+1,right);
	maxv[o]=max(maxv[o*2],maxv[o*2+1]);
	cntv[o]=cntv[o*2]+count(maxv[o*2],o*2+1,mid+1,right);
}
int main()
{
	double x,y;
	int n,m,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
	{
		scanf("%lf%lf",&x,&y);
		xg(y/x,(int)x,1,1,n);
		printf("%d\n",cntv[1]);
	}
	return 0;
}


你可能感兴趣的:(线段树,分情况讨论)