题目描述
小 A 的楼房外有一大片施工工地,工地上有 NN 栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
为了简化问题,我们考虑这些事件发生在一个二维平面上。小 A 在平面上 (0,0)(0,0) 点的位置,第 ii 栋楼房可以用一条连接 (i,0)(i,0) 和 (i,H_i)(i,H
i
) 的线段表示,其中 H_iH
i
为第 ii 栋楼房的高度。如果这栋楼房上任何一个高度大于 00 的点与 (0,0)(0,0) 的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
施工队的建造总共进行了 MM 天。初始时,所有楼房都还没有开始建造,它们的高度均为 00。在第 ii 天,建筑队将会将横坐标为 X_iX
i
的房屋的高度变为 Y_iY
i
(高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。请你帮小 A 数数每天在建筑队完工之后,他能看到多少栋楼房?
输入格式
第一行两个正整数 N,MN,M。
接下来 MM 行,每行两个正整数 X_i,Y_iX
i
,Y
i
。
输出格式
MM 行,第 ii 行一个整数表示第 ii 天过后小 A 能看到的楼房有多少栋。
输入输出样例
输入 #1复制
3 4
2 4
3 6
1 1000000000
1 1
输出 #1复制
1
1
1
2
说明/提示
对于 100%100% 的数据,1 \le X_i \le N1≤X
i
≤N,1 \le Y_i \le 10^91≤Y
i
≤10
9
,1\le N,M \le 10^51≤N,M≤10
5
。
区间查询+修改,线段树没跑了
要看到后面的楼,考虑斜率就好了,后面的楼的斜率只要大于当前楼的斜率,就可以看到了。所以求一个最长递增斜率就可以了。
具体解释看代码吧。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+500;
struct tree {
int l,r;
double k;
int ans;
#define l(p) t[p].l
#define r(p) t[p].r
#define k(p) t[p].k
#define ans(p) t[p].ans
} t[N*4];
void build(int p,int l,int r) {
l(p)=l,r(p)=r,ans(p)=0,k(p)=0;
if(l==r)return;
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
int ask(int p,double maxx) {
//当前区间没有大于maxx的,所以返回0
if(k(p)<=maxx)return 0;
//处理最底层,大于返回1,小于返回0
if(l(p)==r(p)) {
return k(p)>maxx;
} else if(k(p*2)<=maxx) {
//如果左子树全部小于,则取右子树
return ask(p*2+1,maxx);
}//整个区间的答案减去左区间的答案,然后重新查询左区间
return ask(p*2,maxx)+ans(p)-ans(p*2);
}
void change(int p,int x,double val) {
if(l(p)==r(p)) {
ans(p)=1,k(p)=val;
return;
}
int mid=(l(p)+r(p))/2;
if(x<=mid) {
change(p*2,x,val);
} else {
change(p*2+1,x,val);
}
//取区间最大值
k(p)=max(k(p*2),k(p*2+1));
//区间答案等于左区间答案加上右区间比左区间答案大的答案
ans(p)=ans(p*2)+ask(p*2+1,k(p*2));
}
int main() {
int n,m;
scanf("%d %d",&n,&m);
build(1,1,n);
while(m--) {
int x,val;
scanf("%d %d",&x,&val);
change(1,x,1.0*val/x);
printf("%d\n",ans(1));
}
return 0;
}