[BZOJ2957] 楼房重建 (线段树,递归)

题目链接


Solution

经典的一道线段树题,难点在于如何合并节点.
由于题目要求直线要求不相交,则斜率均大于前面的点即为答案.
所以以斜率为权值.
考虑线段树每一个节点维护两个值:

  • \(Max\) 代表当前节点中的最大值.
  • \(Sum\) 代表对于任意一个节点 \(i\) , 其中满足\(w_j>Max(w_{l[i]},w_{l[i]+1}...,w_{r[i]})\)的个数,其中 \(l[i]\),\(r[i]\) 指节点 \(i\) 所在的区间左右端点.\(w\)为斜率.

每一次插入一个节点,它仅会对沿途的最大值和答案产生影响.
然后每次将已经统计好左儿子的 \(Max\) 加入与右儿子中的答案进行比较,递归完成整棵线段树.
然后详情可以看代码.

Code

#include<bits/stdc++.h>
#define N 100008
using namespace std;

struct node{double max;int sum;}sgm[N*4];
int n,m,x,k;

int Calc(int node,double maxn,int l,int r)
{
    int mid=(l+r)>>1;
    if (l==r) return sgm[node].max>maxn;
    if (sgm[node].max<=maxn) return 0;
    //如果当前节点最大值均已不能统计,直接返回.
    if (sgm[node<<1].max<=maxn) return Calc(node<<1|1,maxn,mid+1,r);
    else return sgm[node].sum-sgm[node<<1].sum+Calc(node<<1,maxn,l,mid);
}

void Update(int node,int l,int r,int x,double k)
{
    if (l==r)
    {
        sgm[node].max=k;
        sgm[node].sum=1;
        return;
    }
    int mid=(l+r)>>1;
    if (x<=mid) Update(node<<1,l,mid,x,k);
    else Update(node<<1|1,mid+1,r,x,k);
    sgm[node].max=max(sgm[node<<1].max,sgm[node<<1|1].max);
    sgm[node].sum=sgm[node<<1].sum+Calc(node<<1|1,sgm[node<<1].max,mid+1,r);
    //左边已经处理完,逐层递归返回
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=m; ++i)
    {
        scanf("%d%d",&x,&k);
        Update(1,1,n,x,k*1.0/x);
        printf("%d\n",sgm[1].sum);
    }
}


posted @ 2018-10-03 00:20  Kevin_naticl  阅读(176)  评论(0编辑  收藏  举报