https://www.luogu.org/blog/violet2333/p4198-lou-fang-zhong-jian
题目描述
小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?
输入格式
第一行两个正整数N,M
接下来M行,每行两个正整数Xi,Yi
输出格式 M行,第i行一个整数表示第i天过后小A能看到的楼房有多少栋
输入输出样例
输入 #1
3 4
2 4
3 6
1 1000000000
1 1
输出 #1
1
1
1
2
说明/提示
对于所有的数据1<=Xi<=N,1<=Yi<=10^9
N,M<=100000
emmm~~
经过艰辛万苦,终于弄懂题解啦做出来了。
当然,也有dalao的指导~
线段树维护 序列中的最大斜率,序列长度(即能够看见的楼房个数);
pushup1:更新区间最大的斜率;
pushup2:更新区间的序列长度;
-
修改后
更新每一个区间的最大斜率和长度(划重点);
-
pushup2
①左儿子最大斜率≥右儿子最大斜率:
右儿子全部舍掉
②左儿子最大斜率<右儿子的左端点的斜率:
∴左儿子对右儿子没有影响,直接返回右儿子的序列长度即可
③右儿子只有一个元素:
还用教吗?④不满足以上,左儿子最大斜率<右儿子最大斜率:
递归右儿子。 简称 右儿子的左儿子 为 右左儿子, 右儿子的右儿子 为 右右儿子。 如果右左儿子最大斜率≤左儿子的最大斜率。只递归右右儿子即可。 否则递归右左儿子的序列长度+右儿子序列长度-右左儿子的序列长度。
代码:
#includeusing namespace std; const int N = 100005; struct node { double mx; int len; //序列中的最大斜率,序列长度 } t[N << 2]; int n, m; double a[N]; // i点的斜率 void pushup1(int u) { //更新大区间的最大斜率 t[u].mx = max(t[u << 1].mx, t[u << 1 | 1].mx); } int pushup2(double lmx, int u, int l, int r) { if (t[u].mx <= lmx) return 0; //如果左儿子最大斜率>右儿子最大斜率,右边全部舍掉 if (a[l] > lmx) return t[u].len; //如果右儿子中左端点斜率>左儿子最大斜率,返回右儿子区间长度即可 if (l == r) return a[l] > lmx; //如果只有一个元素,判断该元素斜率与左儿子最大斜率的大小,返回1/0即可; int mid = (l + r) >> 1; //拆分右儿子,继续递归 int s1 = u << 1, s2 = u << 1 | 1; //(大区间右儿子的)左儿子,右儿子 if (t[s1].mx <= lmx) return pushup2(lmx, s2, mid + 1, r); else return pushup2(lmx, s1, l, mid) + t[u].len - t[s1].len; } void change(int u, int l, int r, int x, int y) { if (l == r && l == x) { //序列中只有一个元素 t[u].mx = (double)y / x; t[u].len = 1; return; } int mid = l + r >> 1; if (x <= mid) change(u << 1, l, mid, x, y); if (x > mid) change(u << 1 | 1, mid + 1, r, x, y); pushup1(u); t[u].len = t[u << 1].len + pushup2(t[u << 1].mx, u << 1 | 1, mid + 1, r); //更新大区间的序列长度 /*左儿子序列一定都在大区间中; ∵右儿子会受到左儿子的影响 ∴递归右儿子,更新右儿子中可以看到的数量,加入大区间长度中 */ } int main() { scanf("%d%d", &n, &m); while (m--) { int x, y; scanf("%d%d", &x, &y); a[x] = (double)y / x; //存入斜率 change(1, 1, n, x, y); //修改 printf("%d\n", t[1].len); //输出序列长度 } }