题意转化:动态维护一个单调栈,支持单点修改,整体查询单调栈长度。
使用线段树。对于每个节点,维护 max[root] m a x [ r o o t ] 表示区间最大值, cnt[root] c n t [ r o o t ] 表示区间中的单调栈的长度。在自底向上 maintain 的时候, cnt[root]=cnt[lson]+calc(rson,max[lson]) c n t [ r o o t ] = c n t [ l s o n ] + c a l c ( r s o n , m a x [ l s o n ] ) ,其中 calc(root,value) c a l c ( r o o t , v a l u e ) 表示只保留 >value > v a l u e 的元素, root r o o t 区间的单调栈长度。
我们考虑如何计算 calc c a l c 。当 value>max[lson] v a l u e > m a x [ l s o n ] 时,说明左区间对答案没有贡献,答案为 calc(rson,value) c a l c ( r s o n , v a l u e ) 。否则,答案为 calc(lson,value)+calc(rson,max[lson]) c a l c ( l s o n , v a l u e ) + c a l c ( r s o n , m a x [ l s o n ] ) 。
我们发现 value≤max[lson] v a l u e ≤ m a x [ l s o n ] 的时候两边都要递归下去,时间复杂度时 Θ(n) Θ ( n ) 的,会超时。其实 calc(rson,max[lson]) c a l c ( r s o n , m a x [ l s o n ] ) 就是 cnt[root]−cnt[lson] c n t [ r o o t ] − c n t [ l s o n ] (请读者自行思考)。这样,我们就得到了一个 Θ(mlog22n) Θ ( m log 2 2 n ) 的算法。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef pair<int, int> pii;
const int maxn = 100005;
const int maxm = 1 << 18 | 1;
int n, m, cnt[maxm];
double val[maxn], mx[maxm];
int calc(int u, int l, int r, double x) {
if (l == r) {
if (x < mx[u]) {
return 1;
} else {
return 0;
}
}
int mid = (l + r) >> 1;
int ls = u << 1, rs = ls | 1;
if (x < mx[ls]) {
return cnt[u] - cnt[ls] + calc(ls, l, mid, x);
} else {
return calc(rs, mid + 1, r, x);
}
}
void modify(int u, int l, int r, int x, double y, double z) {
if (l == r) {
mx[u] = y;
cnt[u] = 1;
return;
}
int mid = (l + r) >> 1;
int ls = u << 1, rs = ls | 1;
if (x <= mid) {
modify(ls, l, mid, x, y, z);
} else {
modify(rs, mid + 1, r, x, y, mx[ls]);
}
mx[u] = max(mx[ls], mx[rs]);
cnt[u] = cnt[ls] + calc(rs, mid + 1, r, mx[ls]);
}
int main() {
scanf("%d %d", &n, &m);
for (int a, b, i = 1; i <= m; i++) {
scanf("%d %d", &a, &b);
val[a] = 1. * b / a;
modify(1, 1, n, a, val[a], -1);
printf("%d\n", cnt[1]);
}
return 0;
}