【经典问题】bzoj2957: 楼房重建
经典问题:动态维护上升子序列长度
进阶问题:【经典问题】#176. 栈
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 }
END