版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/83625565
Problem
-
题意就是求一个支持修改的单调栈长度.
-
修改次数 ,序列长度 .
Data constraint
Solution
-
这是一个经典模型.
-
这个是用线段树来实现的,先不妨假设现在要求上升子序列长度.
-
那么如果我们要修改,则直接在线段树上找到对应位置,然后进行修改即可.
-
对于线段树每个节点,维护一个 (这个区间单调栈的长度),一个 (这个区间内数的最大值)
-
现在,当只有最左边一个数时,单调栈长度显然为 .
-
那么归纳,假设左边的单调栈已经构好,现在需要计算当前节点的答案.
-
那么显然 当前答案 = 左边的答案 + 左边最大的数放在 这段区间内得到的单调栈长度
-
而形如 的这个式子,右边的 依旧可以 的时间复杂度内计算出来,做法类似.
-
不妨设 表示在 这段闭区间形成的单调栈栈顶加入 这个点形成的新单调栈长度, 分别表示左右儿子, 表示当前点.
扫描二维码关注公众号,回复: 4440414 查看本文章 -
那么依旧是讨论,假设左区间 的最大值小于等于插入的数,那么贡献显然是
-
否则贡献是 ,这里是整个算法最巧妙的一处.
-
因为整个过程是在线段树上进行的,所以当计算到 这个点的答案时,其所有儿子的答案都已经计算完毕了.
-
那么因为 ,所以实际上 是等价于 .
-
所以整个算法就是 的。
#include <cstdio>
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define M (st + en >> 1)
#define Ls (x << 1)
#define Rs (Ls | 1)
using namespace std;
int n, m, X, Y;
struct node { double v; int ans; } F[400000];
int Calc(int x, int st, int en, double v) {
if (st == en)
return F[x].v > v;
else
return F[Ls].v <= v ? Calc(Rs, M + 1, en, v) : F[x].ans - F[Ls].ans + Calc(Ls, st, M, v);
}
void Make(int x, int st, int en, int p, double v) {
if (st == en)
F[x] = {v, 1};
else
M >= p ? Make(Ls, st, M, p, v) : Make(Rs, M + 1, en, p, v),
F[x].v = max(F[Ls].v, F[Rs].v), F[x].ans = F[Ls].ans + Calc(Rs, M + 1, en, F[Ls].v);
}
int main() {
scanf("%d%d", &n, &m);
F(i, 1, m)
scanf("%d%d", &X, &Y),
Make(1, 1, n, X, (double) Y / X),
printf("%d\n", F[1].ans);
}