小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大---修建,也可以比原来小---拆除,甚至可以保持不变---建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?
Input
第一行两个正整数N,M
接下来M行,每行两个正整数Xi,Yi
Output
M行,第i行一个整数表示第i天过后小A能看到的楼房有多少栋
Sample Input
3 4 2 4 3 6 1 1000000000 1 1
Sample Output
1 1 1 2 数据约定 对于所有的数据1<=Xi<=N,1<=Yi<=10^9 N,M<=100000
Hint
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f
typedef long long LL;
const double eps = 1e-10;
int n, m, sz, num, l[N], r[N], belong[N], cnt[N];
double a[N], block[1010][1010];
void Update(int x) { // 暴力重建块
double tmp = 0.0;
cnt[x] = 0;
for(int i = l[x]; i <= r[x]; i++) // 维护单调递增序列
if(tmp < a[i])
tmp = a[i], block[x][++cnt[x]] = a[i];
}
int Query() {
int ans = 0; double tmp = 0.0;
for(int i = 1; i <= num; i++) { // 二分查找,注意精度
if(!cnt[i])
continue;
int now = cnt[i] - (upper_bound(block[i] + 1, block[i] + 1 + cnt[i], tmp + eps) - block[i] - 1);
if(now)
tmp = block[i][cnt[i]];
ans += now;
}
return ans;
}
int main() {
while(~scanf("%d%d", &n, &m)) {
memset(a, 0, sizeof(a));
memset(cnt, 0, sizeof(cnt));
memset(block, 0, sizeof(block));
sz = sqrt(n);//分块
num = n / sz;
if(n % sz) num++;//不能整除加1
//块的范围
for(int i = 1; i <= num; i++){
l[i] = (i - 1) * sz + 1;
r[i] = i * sz;
}
r[num] = n;
//块编码
for(int i = 1; i <= n; i++)
belong[i] = (i - 1) / sz + 1;
for(int i = 1; i <= m; i++) {
int id, w;
scanf("%d%d", &id, &w);
a[id] = (double)w / id;
Update(belong[id]);
printf("%d\n", Query());
}
}
return 0;
}