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;
}

 

posted @ 2018-12-12 14:41  LeTri  阅读(157)  评论(1编辑  收藏  举报