【题目链接】
【思路要点】
- 令\(B_i=\frac{A_i}{i}\),问题等价于从0开始不断地找右侧第一个\(B_i\)严格大于当前位置的位置,一共可以进行多少轮。
- 考虑用线段树解决本题。
- 为每个节点\(root\)(对应区间\([L,R]\))记录区间最大值\(Max_{root}\)以及从左侧从区间最大值开始,一共可以在\([L,R]\)范围内进行上述过程的轮数\(val_{root}\)。
- 假设我们已经维护好了这样的一系列信息,我们现在要支持一种询问\(query(L,R,pre)\)表示从区间左侧某一个\(B_i\)等于\(pre\)的位置开始,一共可以在\([L,R]\)范围内进行上述过程的轮数。
- 若\(pre≥Max_{leftchild}\),那么\([L,Mid]\)内不会有任何一个数被找到,因此返回\(query(Mid+1,R,pre)\)。
- 否则,\(val_{root}\)就是右侧会被找到的数的个数,返回\(query(L,Mid,pre)+val_{root}\)。
- 可以发现,单次调用\(query\)的复杂度是\(O(LogN)\)的。
- 因此,我们可以借助\(query\)在修改后维护线段树上的信息。
- 时间复杂度\(O(N+MLog^2N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct frac {int x, y; }; bool operator == (frac a, frac b) {return 1ll * a.x * b.y == 1ll * a.y * b.x; } bool operator < (frac a, frac b) {return 1ll * a.x * b.y < 1ll * a.y * b.x; } bool operator > (frac a, frac b) {return 1ll * a.x * b.y > 1ll * a.y * b.x; } bool operator <= (frac a, frac b) {return 1ll * a.x * b.y <= 1ll * a.y * b.x; } bool operator >= (frac a, frac b) {return 1ll * a.x * b.y >= 1ll * a.y * b.x; } struct SegmentTree { struct Node { int lc, rc, val; frac Max; } a[MAXN * 2]; int size, root, n; void build(int &root, int l, int r) { root = ++size; a[root].Max = (frac) {0, 1}; a[root].val = 0; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } int query(int root, int l, int r, frac pre) { if (l == r) return a[root].Max > pre; int mid = (l + r) / 2; if (a[a[root].lc].Max > pre) return query(a[root].lc, l, mid, pre) + a[root].val; else return query(a[root].rc, mid + 1, r, pre); } void update(int root, int l, int r, int mid) { a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max); a[root].val = query(a[root].rc, mid + 1, r, a[a[root].lc].Max); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } void modify(int root, int l, int r, int pos, frac val) { if (l == r) { a[root].Max = val; return; } int mid = (l + r) / 2; if (mid >= pos) modify(a[root].lc, l, mid, pos, val); else modify(a[root].rc, mid + 1, r, pos, val); update(root, l, r, mid); } void modify(int x, frac val) { modify(root, 1, n, x, val); } int query() { return query(root, 1, n, (frac) {0, 1}); } } ST; int n, m; int main() { read(n), read(m); ST.init(n); while (m--) { int x, y; read(x), read(y); ST.modify(x, (frac) {y, x}); writeln(ST.query()); } return 0; }