题面
解法
分块当然是可以做的啦,但是就是比较慢……
考虑一种线段树的做法
维护区间斜率的最大值
mx
m
x
和这段区间的答案
ans
a
n
s
现在问题是如何通过两个子区间的合并求得
ans
a
n
s
显然,在区间合并的时候,我们只需要考虑右儿子的答案就行了
假设我们现在有一个函数
solve(k,mx)
s
o
l
v
e
(
k
,
m
x
)
表示在线段树的点
k
k
上,斜率最大值为的情况下答案是多少
显然,如果左儿子的最大值不大于
mx
m
x
,那么答案就是右儿子的答案
否则,右儿子的答案一定是被左儿子限制的,但是这个答案已经被计算过了,所以答案为
solve(lsonk,mx)+ansk−anslsonk
s
o
l
v
e
(
l
s
o
n
k
,
m
x
)
+
a
n
s
k
−
a
n
s
l
s
o
n
k
为什么后面不是
ansrsonk
a
n
s
r
s
o
n
k
呢??
因为在右儿子的答案不一定对应的是整个区间的答案
举个例子来解释这个问题吧:
假设当前区间中所有数为为
3,4,5,1,2,9
3
,
4
,
5
,
1
,
2
,
9
显然,
ans(k)=4,ans(lson(k))=3,ans(rson(k))=2
a
n
s
(
k
)
=
4
,
a
n
s
(
l
s
o
n
(
k
)
)
=
3
,
a
n
s
(
r
s
o
n
(
k
)
)
=
2
但是,右儿子的答案对应的序列
1,2
1
,
2
是不会被计算在整个区间的答案里面的
时间复杂度:
O(mlog2 n)
O
(
m
log
2
n
)
代码
#include <bits/stdc++.h>
#define N 100010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct SegmentTree {
struct Node {
int l, r, ans;
double mx;
} t[N * 4];
void build(int k, int l, int r) {
t[k] = (Node) {l, r, 0, 0};
if (l == r) return;
int mid = (l + r) >> 1;
build(k << 1, l, mid);
build(k << 1 | 1, mid + 1, r);
}
int calc(int k, double x) {
if (t[k].l == t[k].r) return t[k].mx > x;
if (t[k << 1].mx <= x) return calc(k << 1 | 1, x);
return t[k].ans - t[k << 1].ans + calc(k << 1, x);
}
void modify(int k, int x, double v) {
int l = t[k].l, r = t[k].r;
if (l == r) {t[k].ans = 1, t[k].mx = v; return;}
int mid = (l + r) >> 1;
if (x <= mid) modify(k << 1, x, v);
else modify(k << 1 | 1, x, v);
t[k].mx = max(t[k << 1].mx, t[k << 1 | 1].mx);
t[k].ans = t[k << 1].ans + calc(k << 1 | 1, t[k << 1].mx);
}
} T;
int main() {
int n, m; read(n), read(m);
T.build(1, 1, n);
while (m--) {
int x, y; read(x), read(y);
T.modify(1, x, (double)y / x);
cout << T.t[1].ans << "\n";
}
return 0;
}