BZOJ 2957: 楼房重建
/* 思路非常巧妙的一道题 不难看出线段树维护的是区间内每个点与原点连线的斜率的最大值 考虑合并区间时怎么合并答案 左区间一定会被看到,右区间能被看到的一定大于左区间的最大值 所以可以查询右区间中大于左区间最大值的数的个数来向上合并 每次修改的复杂度为log^2n */ #include<complex> #include<cstdio> using namespace std; const int N=1e5+7; int n,m; int Siz[N<<2]; double Max[N<<2]; int qread() { int x=0; char ch=getchar(); while(ch<'0' || ch>'9')ch=getchar(); while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } int Calc(int l,int r,int rt,double v) { if(Max[rt]<=v)return 0; if(l==r)return Max[rt]>v; int mid=l+r>>1; if(Max[rt<<1]<=v)return Calc(mid+1,r,rt<<1|1,v); return Calc(l,mid,rt<<1,v)+Siz[rt]-Siz[rt<<1]; } void Modify(int l,int r,int rt,int p,double v) { if(l==r) { Max[rt]=v;Siz[rt]=1; return; } int mid=l+r>>1; if(p<=mid)Modify(l,mid,rt<<1,p,v); else Modify(mid+1,r,rt<<1|1,p,v); Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); Siz[rt]=Siz[rt<<1]+Calc(mid+1,r,rt<<1|1,Max[rt<<1]); } int main() { scanf("%d%d",&n,&m); int x,y; while(m--) { x=qread();y=qread(); Modify(1,n,1,x,1.0*y/x); printf("%d\n",Siz[1]); } return 0; }