楼房重建(线段树)

楼房重建

题目描述

小A的楼房外有一大片施工工地,工地上有\(N\)栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。

为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接\((i,0)\)和\((i,H_i)\)的线段表示,其中\(H_i\)为第\(i\)栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。

施工队的建造总共进行了\(M\)天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为\(X_i\)的房屋的高度变为\(Y_i\)(高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?

输入输出格式

输入格式:

第一行两个正整数\(N,M\)

接下来\(M\)行,每行两个正整数\(X_i,Y_i\)

输出格式:

\(M\)行,第\(i\)行一个整数表示第\(i\)天过后小A能看到的楼房有多少栋

输入输出样例

输入样例#1:

3 4
2 4
3 6
1 1000000000
1 1

输出样例#1:

1
1
1
2

说明

对于所有的数据\(1\leq X_i\leq N\),\(1\leq Y_i\leq 10^9\)

\(N,M\leq100000\)

诡异的线段树?
暴力很好想,统计出前面所有斜率的最大值判断当前点的斜率是否大于这个最大值。
如何用线段树进行维护?
由于这个最大值是会随着区间后移变化的,所以线段树中左子树的最大值是会影响右子树的贡献,这导致我们不容易合并两个子树的答案。
考虑线段树内一个值发生变化,左子树对右子树的影响。
如果左子树的最大值比之前的最大值大,那么右子树受到的影响不会改变,直接递归左子树算答案即可(右子树对答案的贡献和上一次一样)。
如果左子树的最大值比之前的最大值小,那么左子树在当前状态下必定不能对答案造成贡献,直接递归右子树即可。
时间复杂度:\(O(能过)\)

#include<bits/stdc++.h>
#define ll(x) (x<<1)
#define rr(x) (x<<1|1)
using namespace std;
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*w;
}
const int N=100010;
int n,m,x,y,ans;
int sgm[4*N];
double maxl[4*N],minl[4*N];
double query(int root,int l,int r,double v){
    if(l>r)return 0;
    if(l==r)return maxl[root]>v;
    if(minl[root]>v)return sgm[root];
    int mid=(l+r)/2;
    if(v>=maxl[ll(root)]) return query(rr(root),mid+1,r,v);
    else return query(ll(root),l,mid,v)+sgm[root]-sgm[ll(root)];
}
void push_up(int x,int l,int r){
    sgm[x]=sgm[ll(x)];
    sgm[x]+=query(rr(x),l,r,maxl[ll(x)]);
    maxl[x]=max(maxl[ll(x)],maxl[rr(x)]);
    minl[x]=min(minl[ll(x)],minl[rr(x)]);
}
void insert(int root,int l,int r,int p,double v){
    if(l>r)return;
    if(l==r&&l==p){sgm[root]=1;maxl[root]=minl[root]=v;return;}
    int mid=(l+r)/2;
    if(p<=mid) insert(ll(root),l,mid,p,v);
    else insert(rr(root),mid+1,r,p,v);
    push_up(root,mid+1,r);
}
int main(){
    n=read();m=read();
    memset(maxl,-127,sizeof(maxl));memset(minl,127,sizeof(minl));
    for(int i=1;i<=m;i++){
        x=read();y=read();insert(1,1,n,x,1.0*y/x);
        printf("%d\n",sgm[1]);
    }return 0;
}

原文地址:https://www.cnblogs.com/lsgjcya/p/9746838.html

时间: 2024-11-05 20:31:17

楼房重建(线段树)的相关文章

bzoj 2957: 楼房重建 线段树

2957: 楼房重建 Time Limit: 10 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些事件发生在一个二维平面上.小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度.

[Luogu P4198]楼房重建(线段树)

题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些事件发生在一个二维平面上.小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段 表示,其中Hi为第i栋楼房的高度.如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的. 施工队的建造总共进行了M天.初始时,所有楼房都还没有开始建

bzoj2957 楼房重建——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2957 线段树维护两个值:cnt 能看到的最多楼房数: mx 最大斜率数: 对于一段区间,从左子区间的角度出发来限制右子区间,得到总区间的 cnt 和 mx: 转移时关注新斜率和左右子区间最大斜率的关系即可. 代码如下: #include<iostream> #include<cstdio> #include<cstring> using namespace std

bzoj2957楼房重建——线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2957 线段树维护原点到楼顶的斜率,可以知道答案就是从原点开始斜率递增的个数: 记录一个mx数组表示这一段上最大的斜率,二分,分类讨论,递归求解: 而且如果要取rs的长度,不是直接取tr[rs],而是总长度减去tr[ls],因为不能从右边一段的起点开始-- 代码如下: #include<iostream> #include<cstdio> #include<cstring

bzoj 2957 楼房重建 (线段树+思路)

链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2957 思路: 用分块可以很简单的过掉,但是这道题也可以用线段树写. 分类讨论左区间最大值对右区间取值的影响,这样每次都只计算左右区间其中一个,复杂度就降成了logn. 实现代码: #include<bits/stdc++.h> using namespace std; #define ll long long #define lson l,m,rt<<1 #define r

[bzoj2957][楼房重建] (线段树)

Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些事件发生在一个二维平面上.小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度.如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的. 施工队的建造总共进行了M天.初始时,所有楼房都

bzoj 1835 基站选址(线段树优化Dp)

Description 题意:有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di 需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci 如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了 如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi 现在的问题是,选择基站的位置,使得总费用最小. Solution 首先可以想到dp,用dp[i][j]表示前i个村庄建了j个通讯站且第j个建在i处 dp[i][j]=min(dp[k][

【BZOJ 2957】 2957: 楼房重建 (线段树)

2957: 楼房重建 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1753  Solved: 841 Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些事件发生在一个二维平面上.小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度.

BZOJ 2957 楼房重建 (线段树)

题目链接  楼房重建 解题思路:我们可以把楼房的最高点的斜率计算出来.那么问题就转化成了实时查询x的个数,满足数列x的左边没有大于等于x的数. 我们可以用线段树维护 设t[i]为如果只看这个区间,可以看到的楼房数量有多少. f[i]为这个区间的x的最大值 更新的时候我们递归讨论. 计算t[i]时,区间的前一半直接套t[i << 1]的结果,但是后一半受前一半区间的最大值的影响,要分开求解. query(i, L, R, val)为当前区间中大于val的数的个数(val并不在这个区间内而在这个区