【题解】
题目描述有误,一栋楼房可见,应满足其最高点到(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; }