[TOC]
前置芝士
- 线段树
Description
一共有 (n) 栋楼,小明坐在 ((0,0)),第 (i) 栋楼房用 ((i,h_i)) 表示。有 (m) 次修改,每次修改一栋楼的高度,可能变高或边矮,修改后输出小明能看到楼的数量。
Solution
稍微分析一下就能得出我们要求一个最长上升的斜率。
考虑如何维护这个看起来不可维护东西。
下面的表示方式可能有所不适,但这是我能想到的一种最清晰的表达方式了
我们设 (mx[l,r]) 表示 ([l,r]) 区间斜率的最大值,(len[l,r]) 表示 ([l,r]) 区间最长合法长度,(k_{i}) 表示第 (i) 栋楼的斜率。
假设当前我们要更新区间 ([l,r]),更小的区间已经被更新了。
设 (mid) 为分解点,(midr) 为区间 ((mid,r]) 的分解点,(lm = mx[l,mid])。
显然左区间的 (len[l,mid]) 每次都要选,我们用递归函数 (Queryr(mid+1,r)) 表示递归去处理 ((mid,r]) 区间(右区间)的结果。
递归时:
- 如果 (lm ge mx(mid, midr]),那么返回 (Queryr(midr+1,r))。这个右区间的左区间已经不能选了。
- 否则,返回 (Queryr(mid+1,midr) + len(mid,r] - len(midr,r])。右区间的左区间的最大值比 (lm) 还大,说明右区间的右区间都能选上,但右区间的左区间不确定,需要递归求解。
下面是几个结束条件:
- 如果 (lm > mx(mid,r]),那么 (len[l,r] = len[l,mid])。考虑实际意义,左边的楼房把右边的全挡住了,右边的就都看不到了。
- 如果 (lm < k_{mid+1}),那么 (len[l,r] = len[l,mid] + len(mid,r])。在右区间中,如果第一栋楼都能被看见,那么右区间能看到的楼在 (l) 处都能看见。
- 如果右区间长度为 (1),直接判断 (lm) 与 (k_r) 的关系即可。
实现细节看代码吧。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n, m;
double a[MAXN];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
namespace Seg {
#define lson i << 1
#define rson i << 1 | 1
struct Tree {
int len; double max;
}tree[MAXN << 2];
void Push_up(int i) { tree[i].max = max(tree[lson].max, tree[rson].max); }
int Queryr(int i, int l, int r, double lmax) {
if(tree[i].max <= lmax) return 0;
if(a[l] > lmax) return tree[i].len;
if(l == r) return a[l] > lmax;
int mid = (l + r) >> 1;
if(tree[lson].max <= lmax) return Queryr(rson, mid + 1, r, lmax);
else return Queryr(lson, l, mid, lmax) + (tree[i].len - tree[lson].len);
}
void Modify(int i, int l, int r, int L, int R, int val) {
if(L <= l && r <= R) {
tree[i].max = (1.0 * val / L);
tree[i].len = 1;
return ;
}
int mid = (l + r) >> 1;
if(mid >= L) Modify(lson, l, mid, L, R, val);
if(mid < R) Modify(rson, mid + 1, r, L, R, val);
Push_up(i);
tree[i].len = tree[lson].len + Queryr(rson, mid + 1, r, tree[lson].max);
}
}
int main()
{
n = read(), m = read();
for(int i = 1, l, r; i <= m; ++i) {
l = read(), r = read();
a[l] = (1.0 * r / l);
Seg::Modify(1, 1, n, l, l, r);
printf("%d
", Seg::tree[1].len);
}
return 0;
}