【经典问题】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能看到的楼房有多少栋

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


题目分析

线段树的巧妙应用:对于每一个线段树区间记录最大值$mx$和以其左端点为起点的答案$ans$。

考虑信息如何合并:对于区间rt,首先肯定继承了rt.lson的答案,再者若rt.rson的最大值rson.mx<lson.mx,也即右区间再不会被访问到了;否则考虑rson的两个子区间,如果rson.lson.mx<lson.mx只需考虑rson.rson,反之答案就是rson.ans(原先rson两个区间的答案和)-rson.lson.ans(现在以rt.lson.mx为起点,需要重新计算rson.lson的贡献;剩下的rson.rson.ans由于是以rson.lson为起点,那么必定是考虑完rson.lson.mx的答案之后的贡献,因此不必改变)+calc(rson.lson)($\text{calc}$即重复以上的统计过程)。

图示如上:

(话说第一发居然因为eps开太小WA了一次)

 1 #include<bits/stdc++.h>
 2 const int maxn = 100035;
 3 const double eps = 1e-50;
 4 
 5 struct node
 6 {
 7     int ans;
 8     double mx;
 9 }f[maxn<<2];
10 int n,m;
11 
12 int read()
13 {
14     char ch = getchar();
15     int num = 0, fl = 1;
16     for (; !isdigit(ch); ch=getchar())
17         if (ch=='-') fl = -1;
18     for (; isdigit(ch); ch=getchar())
19         num = (num<<1)+(num<<3)+ch-48;
20     return num*fl;
21 }
22 int calc(int rt, int l, int r, double c)
23 {
24     if (l==r) return f[rt].mx > c;
25     int mid = (l+r)>>1;
26     if (f[rt<<1].mx-c <= eps) return calc(rt<<1|1, mid+1, r, c);
27     return f[rt].ans-f[rt<<1].ans+calc(rt<<1, l, mid, c);
28 }
29 void modify(int rt, int l, int r, int pos, double c)
30 {
31     if (l==r) f[rt].ans = 1, f[rt].mx = c;
32     else{
33         int mid = (l+r)>>1;
34         if (pos <= mid) modify(rt<<1, l, mid, pos, c);
35         else modify(rt<<1|1, mid+1, r, pos, c);
36         f[rt].mx = std::max(f[rt<<1].mx, f[rt<<1|1].mx);
37         f[rt].ans = f[rt<<1].ans+calc(rt<<1|1, mid+1, r, f[rt<<1].mx);
38     }
39 }
40 int main()
41 {
42     n = read(), m = read();
43     for (; m; --m)
44     {
45         int x = read(), y = read();
46         modify(1, 1, n, x, 1.0*y/x);
47         printf("%d\n",f[1].ans);
48     }
49     return 0;
50 }
扫描二维码关注公众号,回复: 5900360 查看本文章

END

猜你喜欢

转载自www.cnblogs.com/antiquality/p/10712779.html