[数据结构]bzoj2957楼房重建

Description

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

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

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

Input

  第一行两个正整数N,M

  接下来M行,每行两个正整数Xi,Yi

Output

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

Sample Input

3 4

2 4

3 6

1 1000000000

1 1

Sample Output

1

1

1

2

数据约定

  对于所有的数据1<=Xi<=N,1<=Yi<=10^9,N,M<=100000

=========华丽丽的分割线============

虽然是一个清华集训的题目,不过还是可做的嘛。。。

一开始的时候自己写了一个程序,然后怎么都没有调出来。

考虑本题,给出一个数列,然后要求支持单点修改以及询问比自己左边所有数都大的数就几个。

考虑线段树,维护出一段中的高度最大值以及别的数都不考虑的情况下(这个一定不能漏)比这样的数有几个。

考虑合并两个线段,高度的最大值是很容易合并的,直接取一个max就可以了。

对于贡献度,我们发现一个线段的左半部分所有满足的数在原来的线段中一定满足,于是我们只需要考虑右半部分。

我们写一个函数calc(node,k)表示node这个线段在左侧有一个大小为k的数的时候内部满足条件的数的个数。

于是发现如果这个线段的左半部分的最大值小于等于k,那么左半部分贡献就是0,直接返回calc(node*2+1,k),

如果左半部分最大值大于k,那么右半部分原本的个数是不会变的,然后再加上calc(node*2,k)就可以了,

时间复杂度O(nlog^2n),

听说这题卡精度,在吕爷爷的帮助下我学会了fraction,

 1 #include <bits/stdc++.h>
 2 #define Maxn 100007
 3 using namespace std;
 4 int read()
 5 {
 6     int x=0,f=1;char ch=getchar();
 7     while (ch<‘0‘||ch>‘9‘){if (ch==‘-‘) f=-1;ch=getchar();}
 8     while (ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
 9     return x*f;
10 }
11 struct fraction
12 {
13     int dx,dy;
14 };
15 bool operator >(fraction a, fraction b)
16 {
17     return (1LL*a.dx*b.dy>1LL*a.dy*b.dx);
18 }
19 bool operator >=(fraction a, fraction b)
20 {
21     return (1LL*a.dx*b.dy>=1LL*a.dy*b.dx);
22 }
23 bool operator <(fraction a, fraction b)
24 {
25     return (1LL*a.dx*b.dy<1LL*a.dy*b.dx);
26 }
27 bool operator <=(fraction a, fraction b)
28 {
29     return (1LL*a.dx*b.dy<=1LL*a.dy*b.dx);
30 }
31 int n,m;
32 struct seg
33 {
34     int lx,rx,cnt;
35     fraction hmax;
36 };
37 seg tree[Maxn*4];
38 void build(int node, int l, int r)
39 {
40     tree[node].lx=l,tree[node].rx=r,tree[node].cnt=0;
41     tree[node].hmax=(fraction){0,1};
42     if (tree[node].lx==tree[node].rx) return;
43     int mid=(l+r)/2;
44     build(node*2,l,mid),build(node*2+1,mid+1,r);
45 }
46 int calc(int node, fraction h)
47 {
48     if (tree[node].hmax<=h) return 0;
49     if (tree[node].lx==tree[node].rx) return 1;
50     if (tree[node*2].hmax<=h) return calc(node*2+1,h);
51     else return tree[node].cnt-tree[node*2].cnt+calc(node*2,h);
52 }
53 void update(int node, int pos, fraction h)
54 {
55     if (tree[node].rx<pos) return;
56     if (tree[node].lx>pos) return;
57     if (tree[node].lx==tree[node].rx)
58     {
59         tree[node].hmax=h;
60         tree[node].cnt=1;
61         return;
62     }
63     update(node*2,pos,h),update(node*2+1,pos,h);
64     tree[node].hmax=max(tree[node*2].hmax,tree[node*2+1].hmax);
65     tree[node].cnt=tree[node*2].cnt+calc(node*2+1,tree[node*2].hmax);
66 }
67 int main()
68 {
69     n=read(),m=read();
70     build(1,1,n);
71     while (m--)
72     {
73         int x=read(),y=read();
74         update(1,x,(fraction){y,x});
75         printf("%d\n",tree[1].cnt);
76     }
77     return 0;
78 }

代码如下:

时间: 2024-07-30 16:20:07

[数据结构]bzoj2957楼房重建的相关文章

bzoj2957: 楼房重建,分块

题目链接:2957: 楼房重建 分块没学过的可以看,分块入门. 题解:把房子分成√n块每块里面维护一个递增的子序列,每次更新之后,在每一个小块内二分查找第一个大于前面最大的斜率,开始斜率为0,每次找完一块更新一次,暴力加进答案. #include<bits/stdc++.h> #include<set> #include<iostream> #include<string> #include<iomanip> #include<vector

bzoj2957 楼房重建

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

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

Description 小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

[数据结构]之栈的插入,归并以及快速排序

栈的排序算法: 第一种,类似于插入排序,(允许额外利用一个栈),每一步的循环不变式是temp中的元素是有序的.直接上代码: 第二种排序:快速排序,快速排序每次根据一个partition将栈分为两个部分,于是下面的代码中使用了两个栈来作为辅助栈. 第三种排序:归并排序.直接上代码,比较简单:

[数据结构]链表的基本操作

链表 可以用任意一组存储单元来存储单链表中数据元素(存储单元可以是不连续的),而且,除了存储每个数据元素ai的值以外,还必须存储指示其直接后继元素的信息.这两部分信息组成的数据元素ai的存储映像称为结点.N个结点链在一起被称为链表,当结点只包含其后继结点的信息的链表就被称为单链表. 链表定义如下数据类来存储结点信息: public class Node { Node next = null; int data; public Node(int data){ this.data = data; }

bzoj2957楼房重建——线段树

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

bzoj2957楼房重建

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2957 线段树.每个点记录斜率,要一个单增的序列长度(从1开始). 线段树每个点记录自己区间的max和单增长度,pushup的时候通过mx [ ls ]判断往哪个儿子递归. 需要注意的是当右儿子的长度全部可以加上是,不能加s [ rs ],因为不一定全在总的单增序列上(只有rs中大于mx[ls]的那一部分): 所以右儿子的贡献应该是原先总区间的s减去左儿子的s,因为单增序列从最左端开始,总序

成都磨子桥技工学校 / 数据结构 Challenge 6(楼房重建)

描述 给一个长为N的数列,有M次操作,每次操作是以下两种之一: (1)修改数列中的一个数 (2)求数列中有多少个数比它前面的数都大 题解 不会就去参考了一下前辈的题解,说是楼房重建的原题,又去参考了楼房重建的题解(滑稽). 对于本题,建立线段树,区间记录最大值和有多少数比前面的数都大的数的个数sum. 考虑合并区间时第二个信息,如果右区间最大值小于等于左区间最大值,那么直接取左区间信息,应该比较显然右区间的任何一个元素都比左区间最大值小. 不然的话就要把左区间最大值val带入右区间查询,如果现在