一道 sb 题的 sb 做法
有 座城市,第 座城市里宝石的交易价格为 。当你经过第 座城市时,你可以以 的价格购买或卖出一个宝石。在任意时刻,你最多只能携带一个宝石。
有 次操作,操作分为两种:
询问依次经过 到 的城市的城市能获得的最大收益。最后离开编号为 的城市时,身上不能携带宝石;
将 到 的宝石价格修改为一个首项为 ,公差为 的等差数列;
,,,
看到这个题感觉就是线段树,所以我们用线段树做。
主要的问题是询问操作不好维护,我们要思考怎么用两个儿子的答案合并出父亲的答案。
如果我们对于一个线段树节点只记录它这个区间的最大收益,显然是不能合并的。为什么?因为有种情况是走出左儿子后还剩下一个宝石,把这个宝石留到右儿子去卖。
为了处理这个情况,我们维护四个信息 ,一维的 表示进入这个区间,即到了 时时有没有宝石,二维的表示出这个区间的时候有没有宝石。然后合并就非常简单了:
接下来要考虑的问题就是修改。我们要想办法让修改区间覆盖一个线段树区间时 修改。
其实很简单,两种情况:
- $ y<0$,这时等差数列是递减的,所以
- 前面买后面卖肯定是亏的,所以 ;
- 最后一个最小所以肯定在最后一个买,所以 ;
- 第一个最大所以在第一个买最赚,所以 ;
- 在第一个卖,最后一个买最赚,所以
- ,等差数列递增,
读者自算不难;
然后这题就做完了。
CODE :
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int $() {
int x = 0, f = 0; char c = 0;
while (!isdigit(c)) f |= c == '-', c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c & 15), c = getchar();
return f ? -x : x;
}
#define N 200010
#define INF (1e16)
int n, m, a[N];
struct node {
int a[2][2];
node (int x = -INF, int y = -INF, int z = -INF, int w = -INF) {
a[0][0] = x, a[0][1] = y, a[1][0] = z, a[1][1] = w;
}
};
node F(node x, node y) {
node r;
for (int i = 0; i <= 1; i ++) {
for (int j = 0; j <= 1; j ++) {
for (int k = 0; k <= 1; k ++) {
r.a[i][j] = max(r.a[i][j], x.a[i][k] + y.a[k][j]);
}
}
}
return r;
}
#define lid (id << 1)
#define rid (id << 1 | 1)
#define mid (l + r >> 1)
struct Seg { node s; int x, y; }tr[N * 4];
void build(int id, int l, int r) {
if (l == r) {
return tr[id].s = node(0, -a[l], a[l], 0), void();
}
build(lid, l, mid), build(rid, mid + 1, r);
tr[id].s = F(tr[lid].s, tr[rid].s);
}
void Calc(int id, int l, int r) {
int x = tr[id].x, y = tr[id].y;
if (y < 0) {
tr[id].s = node(0ll, -(x + (r - l) * y), x, -y * (r - l));
} else {
tr[id].s = node(y * (r - l), -x, y * (r - l) + x, 0ll);
}
}
void Down(int id, int l, int r) {
if (l == r) return;
if (tr[id].x == 0 && tr[id].y == 0) return;
tr[lid].x = tr[id].x;
tr[rid].x = tr[id].x + tr[id].y * (mid - l + 1);
tr[lid].y = tr[rid].y = tr[id].y;
tr[id].x = tr[id].y = 0;
Calc(lid, l, mid), Calc(rid, mid + 1, r);
}
void Add(int id, int l, int r, int L, int R, int x, int y) {
Down(id, l, r);
if (L <= l && r <= R) {
return tr[id].x = x, tr[id].y = y, Calc(id, l, r);
}
if (L <= mid) Add(lid, l, mid, L, R, x, y);
if (R > mid) Add(rid, mid + 1, r, L, R, x + max(0ll, mid - max(l, L) + 1) * y, y);
tr[id].s = F(tr[lid].s, tr[rid].s);
}
node Ask(int id, int l, int r, int L, int R) {
Down(id, l, r);
if (L <= l && r <= R) return tr[id].s;
node lr, rr; int vl = 0, vr = 0;
if (L <= mid) lr = Ask(lid, l, mid, L, R), vl = 1;
if (R > mid) rr = Ask(rid, mid + 1, r, L, R), vr = 1;
if (!vl) return rr;
if (!vr) return lr;
return F(lr, rr);
}
signed main() {
freopen("gem.in", "r", stdin);
freopen("gem.out", "w", stdout);
n = $(), m = $();
for (int i = 1; i <= n; i ++) a[i] = $();
build(1, 1, n);
for (int i = 1; i <= m; i ++) {
int op = $(), l = $(), r = $(), x, y;
if (op == 1) {
printf("%lld\n", Ask(1, 1, n, l, r).a[0][0]);
} else {
x = $(), y = $(), Add(1, 1, n, l, r, x, y);
}
}
return 0;
}一道 sb 题的 sb 做法
https://ybwa.github.io/p/cb8706ba/