题目大意
一共有 n n n栋楼,初始高度都是0,给出 m m m次操作,每次操作将楼 x x x的高度改为 y y y,可能是建楼也有可能是拆楼,试求每次更改之后处于 ( 0 , 0 ) (0,0) (0,0)位置的小A能看见几座楼,能看见一栋楼即代表在该楼之前的所有楼的楼顶的斜率都小于这座楼楼顶的斜率。
分析
快一个月没写博客了,写写纪念纪念。这道题的题意我真是吐了,感觉表述的不清不楚的。明白题意之后,并没有很快的想到正解,但起码有了点思路。本质就是用线段树维护 [ l , r ] [l,r] [l,r]区间内的最大斜率, 以及这段区间内能看到的楼数。然后每次修改相当于一次单点修改,维护一个区间内的能看到的楼数还是有技巧的,详细见代码。
Code
#include <cstdio>
#define ld long double
using namespace std;
const int N = 1e5 + 10;
struct Edge{
ld mx;
int l;
} t[N << 2];
int n,m;
ld max(ld a,ld b) {return a > b ? a : b;}
int count(int l,int r,int x,ld k)
{
if (l == r) return t[x].mx > k;
int mid = l + r >> 1;
if (t[x << 1].mx <= k) return count(mid + 1,r,x << 1 | 1,k);//倘若左边区间没有任何一栋楼能被看到那么直接维护右边区间。
return t[x].l - t[x << 1].l + count(l,mid,x << 1,k);//否则求出左边区间有多少楼能被看到,那么由于右边区间的答案本来就是通过左区间求出来的所以左区间的斜率值均没改变的情况下右边区间在这整个大区间中的相对答案也是不会改变的,所以只用维护左区间并加上右区间原来的答案。
}
void modify(int l,int r,int x,int k,ld v)
{
if (l == r)
{
t[x].mx = v,t[x].l = 1;//单点修改
return;
}
int mid = l + r >> 1;
if (k <= mid) modify(l,mid,x << 1,k,v); else modify(mid + 1,r,x << 1 | 1,k,v);
t[x].mx = max(t[x << 1].mx,t[x << 1 | 1].mx);
t[x].l = t[x << 1].l + count(mid + 1,r,x << 1 | 1,t[x << 1].mx);//维护一个区间的能看到的楼的个数,关键是要搞懂count函数部分。
}
int main()
{
scanf("%d%d",&n,&m);
while (m --)
{
int x,y;
scanf("%d%d",&x,&y);
modify(1,n,1,x,(ld)y / x);
printf("%d\n",t[1].l);
}
return 0;
}