luogu 楼房重建
- problem
- solution
- code
problem
洛谷链接
solution
非常巧妙的一道题,对线段树的运用很灵活。
显然这个与原点的连线可以想到将每个点转化为与原点连线形成的直线斜率。
答案其实就是:从第一个点开始选,后一个斜率比前面大的必须选,否则,即小于等于则必须不选的选择个数。
区间是固定的,且左边的一些信息影响右边,每次还要单点修改,可以联想到线段树。
这个线段树没有懒标记,自然也没有下放标记操作。唯一的难点就是在于合并操作的实现。
线段树区间 [l,r][l,r][l,r] 维护从 lll 开始选,按照答案的规则选出的个数答案。
最终输出线段树节点 111 表示 [1,n][1,n][1,n] 区间的答案即可。
显然,一段区间的第一个一定被选,一段区间中的最大值也一定被选。
而一段区间的最大值会影响后面区间的可选值,所以还要维护一个区间 [l,r][l,r][l,r] 中最大的斜率。
考虑左右儿子具体怎么合并给父亲。
首先,左儿子合并上去后做的是开头部分,所以左儿子的答案一定贡献。
其次,左儿子一定会选其区间内的斜率最大值,而右儿子的斜率都必须大于这个最大值。
这里,我们采取递归实现合并方式,pushup(now,l,r,k)
表示要求线段树 nownownow 节点代表的区间 [l,r][l,r][l,r] 选择的斜率必须大于 kkk 的答案。
一开始是要求右儿子选择整体要大于左儿子的斜率最大值的。
t[now].ans = t[lson].ans + pushup( rson, mid + 1, r, t[lson].Max );
然后开始递归考虑右儿子的子树,即具体的 pushup
实现。
-
如果节点内的最大值都不超过要求的斜率,直接返回。
if( t[now].Max <= k ) return 0;
-
如果左区间的最大值都不超过要求的斜率,那么直接找右区间即可。
if( t[lson].Max <= k ) return pushup( rson, mid + 1, r, k );
-
否则,就要去找左区间的答案。左区间既然有些要被选,那么就没有影响右区间的开始,直接统计右区间的答案。
而右区间的答案不是
t[rson].ans
而是t[now].ans-t[lson].ans
。因为右区间可能有些会被左区间挡住不能选,就只能换一种方法表示了。
return pushup( lson, l, mid, k ) + t[now].ans - t[lson].ans;
-
走到叶子节点,就直接判断是否大于要求的斜率即可。
if( l == r ) return t[now].Max > k;
-
trick
可以记录一下横坐标为 lll 的点与原点形成直线的斜率。
如果这个斜率就一定大于要求值的话,那么就相当于从 lll 开始按照规则选的个数。
即线段树上 [l,r][l,r][l,r] 区间维护的信息,那么直接返回就不用再往下操作了。
if( g[l] > k ) return t[now].ans;
code
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
struct node { double Max; int ans; }t[maxn << 2];
double g[maxn];#define lson now << 1
#define rson now << 1 | 1
#define mid ( ( l + r ) >> 1 )int pushup( int now, int l, int r, double k ) {if( t[now].Max <= k ) return 0;if( k < g[l] ) return t[now].ans;if( l == r ) return t[now].Max > k;if( t[lson].Max <= k ) return pushup( rson, mid + 1, r, k );else return pushup( lson, l, mid, k ) + t[now].ans - t[lson].ans;
}void modify( int now, int l, int r, int x, int y ) {if( l == r ) { t[now].Max = 1.0 * y / x, t[now].ans = 1; return; }if( x <= mid ) modify( lson, l, mid, x, y );else modify( rson, mid + 1, r, x, y );t[now].Max = max( t[lson].Max, t[rson].Max );t[now].ans = t[lson].ans + pushup( rson, mid + 1, r, t[lson].Max );
}int main() {int n, m, x, y;scanf( "%d %d", &n, &m );while( m -- ) {scanf( "%d %d", &x, &y );g[x] = 1.0 * y / x;modify( 1, 1, n, x, y );printf( "%d\n", t[1].ans );}
}