楼房重建 线段树
题解:
一栋楼是否已经被前面的楼房遮挡住,可以利用斜率来判断。
因此将原序列转化为斜率。
然后直接用线段树维护一下每个区间最多能看见多少楼房即可。
在更新区间的时候需要在线段树上二分,因为左区间是肯定可以取的,然后设左区间的最大值为k,
那么右区间的贡献就是大于k的最长上升子序列,二分即可。
建议画画图,还是很好理解的。
注意:这里其实并不是维护的传统意义上的最长递增子序列,因为这里的子序列强制从第一个开始了,所以代码中的注释其实有点问题,,(算法是对的)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 100100 5 #define ac 600000 6 int n, m; 7 int tree[ac], l[ac], r[ac];//维护最长上升子序列和 8 double mx[ac]; 9 10 inline int read() 11 { 12 int x = 0;char c = getchar(); 13 while(c > '9' || c < '0') c = getchar(); 14 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 15 return x; 16 } 17 18 int cal(int x, double k)//当前点,大于k的最长上升子序列 19 {//计算在一段区间内大于k的可以被看见的有多少个 20 if(l[x] == r[x]) return mx[x] > k; 21 if(mx[x] <= k) return 0; 22 if(mx[x * 2] < k) return cal(x * 2 + 1, k); 23 else return tree[x] - tree[x * 2] + cal(x * 2, k); 24 } 25 26 void update(int x) 27 { 28 mx[x] = max(mx[x * 2], mx[x * 2 + 1]); 29 tree[x] = tree[x * 2] + cal(x * 2 + 1, mx[x * 2]); 30 } 31 32 void build(int x, int ll, int rr) 33 { 34 l[x] = ll, r[x] = rr; 35 if(ll == rr) return ; 36 int mid = (ll + rr) >> 1; 37 build(x * 2, ll, mid); 38 build(x * 2 + 1, mid + 1, rr); 39 } 40 41 void change(int x, int w, double t)//当前点,目标点, 改成什么 42 {//单点修改并维护新值 43 if(l[x] == r[x]) 44 { 45 mx[x] = t; 46 tree[x] = 1; 47 return ; 48 } 49 int mid = (l[x] + r[x]) >> 1; 50 if(w <= mid) change(x * 2, w, t); 51 else change(x * 2 + 1, w, t); 52 update(x); 53 } 54 55 void pre() 56 { 57 n = read(), m = read(); 58 build(1, 1, n); 59 } 60 61 void work() 62 { 63 int a, b; 64 for(R i = 1; i <= m; i ++) 65 { 66 a = read(), b = read(); 67 change(1, a, (double)b / (double)a);//这个也要用double 68 printf("%d\n", tree[1]); 69 } 70 } 71 72 int main() 73 { 74 // freopen("in.in", "r", stdin); 75 pre(); 76 work(); 77 // fclose(stdin); 78 return 0; 79 }