【DS,性质题】P5069 [Ynoi2015] 纵使日薄西山

· · 题解

提供一个线段树的做法。

首先 a_{x-1}-1,a_x-1,a_{x+1}-1 不会改变三者的大小关系, a_x 依然是这三个数中最大的,因此左右两边必不会选到,要让 a_x=0 只能对着 x 操作 a_x 次(然后它左右两边顺理成章的也变成了零)。

所以原题实际上是要维护一个被减的数的集合,这些数在原序列互不相邻且符合操作的要求,然后输出这些数的和。

考虑用线段树维护这个集合的和。

首先叶子的答案是本身,然后我们考虑合并两个区间。

发现如果 左区间的右端点没有被选 或者 右区间的左端点没有被选,这两个区间是互不影响的,答案直接相加即可。

但是像下面的两个区间:( X 表示被选进集合中)

[1 3 4] + [5 5 4] -> [X . X] + [X . X] -> [. X . X . X]

因为 X 互不相邻,合并的时候要强制一个区间的端点不选(即当它不存在)。

因此我们要计算四个值。

同时为了辅助合并,还要记 $lm(l,r,0/1/2/3)$ 表示 $[l,r]$ 这个区间在 $0/1/2/3$ 这四种情况下左端点有没有被选, $rm(l,r,0/1/2/3)$ 右端点同理。 分类讨论维护即可。 ```cpp #include <bits/stdc++.h> using namespace std; typedef long long ll; int read() { char ch = getchar(); int x = 0, pd = 0; while (ch < '0' || ch > '9') pd |= ch == '-', ch = getchar(); while ('0' <= ch && ch <= '9') x = x * 10 + (ch ^ 48), ch = getchar(); return pd ? -x : x; } const int maxn = 100005; int n, m, a[maxn]; #define ls (o << 1) #define rs (o << 1 | 1) #define lson ls,l,mid #define rson rs,mid+1,r bool lm[maxn << 2][4], rm[maxn << 2][4]; ll s[maxn << 2][4]; void pushup(int o, int l, int r) { int mid = (l + r) >> 1; if (rm[ls][0] && lm[rs][0]) { if (a[mid] >= a[mid + 1]) { lm[o][0] = lm[ls][0], rm[o][0] = rm[rs][1]; s[o][0] = s[ls][0] + s[rs][1]; } else{ lm[o][0] = lm[ls][2], rm[o][0] = rm[rs][0]; s[o][0] = s[ls][2] + s[rs][0]; } } else lm[o][0] = lm[ls][0], rm[o][0] = rm[rs][0], s[o][0] = s[ls][0] + s[rs][0]; if (rm[ls][1] && lm[rs][0]) { if (a[mid] >= a[mid + 1]) { rm[o][1] = rm[rs][1]; s[o][1] = s[ls][1] + s[rs][1]; } else { rm[o][1] = rm[rs][0]; s[o][1] = s[ls][3] + s[rs][0]; } } else rm[o][1] = rm[rs][0], s[o][1] = s[ls][1] + s[rs][0]; if (rm[ls][0] && lm[rs][2]) { if (a[mid] >= a[mid + 1]) { lm[o][2] = lm[ls][0]; s[o][2] = s[ls][0] + s[rs][3]; } else { lm[o][2] = lm[ls][2]; s[o][2] = s[ls][2] + s[rs][2]; } } else lm[o][2] = lm[ls][0], s[o][2] = s[ls][0] + s[rs][2]; if (rm[ls][1] && lm[rs][2]) { if (a[mid] >= a[mid + 1]) s[o][3] = s[ls][1] + s[rs][3]; else s[o][3] = s[ls][3] + s[rs][2]; } else s[o][3] = s[ls][1] + s[rs][2]; } void build(int o, int l, int r) { if (l == r) { s[o][0] = a[l] = read(); lm[o][0] = rm[o][0] = 1; return; } int mid = (l + r) >> 1; build(lson), build(rson), pushup(o, l, r); } void upd(int o, int l, int r, int x) { if (l == r) { s[o][0] = a[l]; return; } int mid = (l + r) >> 1; if (x <= mid) upd(lson, x); else upd(rson, x); pushup(o, l, r); } int main() { n = read(); build(1, 1, n); m = read(); while (m--) { int x = read(), y = read(); a[x] = y, upd(1, 1, n, x); printf("%lld\n", s[1][0]); } return 0; } ```