SJTU 4037 买房

4037 买房

description

拔娜娜很有钱,现在要给娜娜买房。
这个世界的房子都建在二维平面上,总共有 n(n<=10^5)栋房子,每栋房子都是线段,第i房子是线段(i, 0)-(i, h_i),h_i是第i栋房子的高度。
娜娜站在原点向x正半轴看,如果从原点到某栋楼楼顶的连线不与其他的楼相交(交在之前的楼顶的话也看不到),那么娜娜就看得到这栋楼。
一开始,每栋房子高度都是0。每天,都会有某一栋房屋的高度都会被修改。
娜娜很任性,看到的高度大于0房子都要买,如果高度为0,就看不到。 求对于接下来m(m <= 100000)天,如果拔娜娜在这天给娜娜买房子,他需要买多少栋。
input format
数据第一行表示数据组数T(T <= 3)
每组数据的第一行两个整数n, m,意义如题目描述。
之后m行,每行两个整数id, height(id <= n, 1 <= height <= 10^9),表示id号房子的高度改成了height,注意height >= 1,也就是说,改完之后的房子高度肯定不是0。
output format
输出m个整数,表示对于每一天,拔娜娜需要买房的话会买几栋。
不同组数据之间不需要空行。
sample input
1
3 4
2 4
3 6
1 1000000000
1 1
sample output
1
1
1
2
limits
对于20%的数据,n <= 10000
对于另外20%的数据,数据均为随机生成

题目分析

此题是用线段树做的,反正当时学姐要我看这道题的时候,我是不晓得怎么做的。后面学姐讲完后才明白。
题目的大概意思就是说每次给出一个操作,会改变其中一栋楼的高度,改变之后再求一次从原点可以看到的楼的个数。而当修改x位置的楼的高度时,在x前的都不用管,只要看在x后面会有什么影响。于是我们可以对x~n进行线段树的相关操作。对于整个区间,他的贡献由他的左右儿子决定。
先来讨论一下左儿子,如果左儿子中有斜率大于当前修改的斜率(毕竟看不看得到是由某个点上的斜率决定的)那么就往左儿子递归。
再来讨论右儿子,如果左儿子中最大的斜率都没有大于当前修改的斜率,那么我们就只能看右儿子的,依次递归下去。

程序代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ls (x<<1)
#define rs ((x<<1)|1)
#define midd (l+r>>1)
using namespace std;
int n,m,t;
struct arr{
    int l,r,sumn;
    double maxn;
}tree[200500*5];
inline int read(){
    int x=0,w=1;char ch;
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') w=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
    return x*w;
}
int change(int x,int l,int r,double v){
    if(l==r) return (int)(tree[x].maxn>v);
    if(tree[ls].maxn<=v) return change(rs,midd+1,r,v);//左边都没有,那往右边递归
    return tree[x].sumn-tree[ls].sumn+change(ls,l,midd,v);//处理答案。原来区间的答案减去原来左儿子的答案加上先在左儿子的答案
}
void query(int x,int l,int r,int p,double v){
    if(l==r) {tree[x].maxn=v;tree[x].sumn=1;return;}
    if(p<=midd) query(ls,l,midd,p,v);
    else if(p>midd)  query(rs,midd+1,r,p,v);
    tree[x].maxn=max(tree[ls].maxn,tree[rs].maxn);
    tree[x].sumn=tree[ls].sumn+change(rs,midd+1,r,tree[ls].maxn);
}
int main(){
    t=read();
    for(register int k=1;k<=t;++k){
        n=read();m=read();
        for(register int i=1;i<=5*n;++i) tree[i].maxn=tree[i].sumn=0;
        for(register int i=1;i<=m;++i){
            int xx=read(),yy=read();
            query(1,1,n,xx,(double)yy/xx);
            printf("%d\n",tree[1].sumn);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值