【题目链接】
【思路要点】
- 首先我们来考虑只有加减怎么做,考虑一下最终的函数图像,它应该始终由一条处于中间的斜率为1的线段与左右各一条斜率为0的线段构成。简单描述一下这个图像,我们可以在\(O(N+Q)\)的时间内解决只有加减的情况。
- 考虑加入乘法,由于乘法同样只和数值有关,而与坐标无关,因此最终函数图像同样始终由一条处于中间的斜率为\(x\)的线段与左右各一条斜率为0的线段构成。沿用上面的做法,我们依然可以在\(O(N+Q)\)的时间内解决只有加、减和乘的情况。
- 在加入\(@\)运算前,我们先分析一下每种操作对某一段函数图像\(y=k(x-l)+b(x\in[l,r])\)的影响。
- 1、\(+a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=k(x-l)+(b+a)(x\in[l,r])\)。
- 2、\(-a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=k(x-l)+(b-a)(x\in[l,r])\)。
- 3、\(*a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=(k*a)(x-l)+(b*a)(x\in[l,r])\)。
- 4、\(@a\),\(y=k(x-l)+b(x\in[l,r])\Rightarrow y=(k+a)(x-l)+(b+l*a)(x\in[l,r])\)。
- 可以看到,\(@\)运算会使斜率进行加法,因此最终的函数可能分为\(O(N)\)段。
- 用线段树维护分段函数集合,上面的各种修改线段树均能够支持。
- 注意到在任何时刻,最终函数为单调函数,因此可以在修改前检查最左/右侧可能产生越界的函数,对其单独进行修改,这样同样避免了可能爆long long的问题。
- 询问时在得到的分段函数上二分即可。
- 时间复杂度\(O(NLogN+QLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXP = 400005; 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(""); } void getopt(char &x) { x = getchar(); while (x != '+' && x != '-' && x != '*' && x != '@') x = getchar(); } struct SegmentTree { struct Node { int lc, rc; long long indexadd, indexfrm, indexmul; long long slopeadd, slopemul; } a[MAXP]; struct Line { int from, to; long long index, slope; } val[MAXN * 2]; int root, size, n, nl, nr, vl, vr; void pushdown(int root) { if (a[root].lc) { int now = a[root].lc; a[now].indexadd *= a[root].indexmul; a[now].indexadd += a[root].indexadd; a[now].indexfrm *= a[root].indexmul; a[now].indexfrm += a[root].indexfrm; a[now].indexmul *= a[root].indexmul; a[now].slopeadd *= a[root].slopemul; a[now].slopeadd += a[root].slopeadd; a[now].slopemul *= a[root].slopemul; } if (a[root].rc) { int now = a[root].rc; a[now].indexadd *= a[root].indexmul; a[now].indexadd += a[root].indexadd; a[now].indexfrm *= a[root].indexmul; a[now].indexfrm += a[root].indexfrm; a[now].indexmul *= a[root].indexmul; a[now].slopeadd *= a[root].slopemul; a[now].slopeadd += a[root].slopeadd; a[now].slopemul *= a[root].slopemul; } a[root].indexadd = 0; a[root].indexfrm = 0; a[root].indexmul = 1; a[root].slopeadd = 0; a[root].slopemul = 1; } void build(int &root, int l, int r) { root = ++size; a[root].indexadd = 0; a[root].indexfrm = 0; a[root].indexmul = 1; a[root].slopeadd = 0; a[root].slopemul = 1; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } void init(int x, int l, int r) { n = x * 2 + 1; nl = nr = x + 1; vl = l, vr = r; val[x + 1] = (Line) {vl, vr, vl, 1}; build(root, 1, n); } void inc(int root, int l, int r, int ql, int qr, int x) { if (l == ql && r == qr) { a[root].indexadd += x; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) inc(a[root].lc, l, mid, ql, min(mid, qr), x); if (mid + 1 <= qr) inc(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x); } void times(int root, int l, int r, int ql, int qr, int x) { if (l == ql && r == qr) { a[root].indexadd *= x; a[root].indexfrm *= x; a[root].indexmul *= x; a[root].slopeadd *= x; a[root].slopemul *= x; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) times(a[root].lc, l, mid, ql, min(mid, qr), x); if (mid + 1 <= qr) times(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x); } void modify(int root, int l, int r, int ql, int qr, int x) { if (l == ql && r == qr) { a[root].indexfrm += x; a[root].slopeadd += x; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) modify(a[root].lc, l, mid, ql, min(mid, qr), x); if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x); } void clarify(int root, int l, int r, int pos) { if (l == r) { val[l].index *= a[root].indexmul; val[l].index += a[root].indexadd; val[l].index += a[root].indexfrm * val[l].from; val[l].slope *= a[root].slopemul; val[l].slope += a[root].slopeadd; a[root].indexadd = 0; a[root].indexfrm = 0; a[root].indexmul = 1; a[root].slopeadd = 0; a[root].slopemul = 1; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= pos) clarify(a[root].lc, l, mid, pos); else clarify(a[root].rc, mid + 1, r, pos); } long long calc(int pos, int x) { return (x - val[pos].from) * val[pos].slope + val[pos].index; } void inc(int x) { clarify(root, 1, n, nr); if (calc(nr, val[nr].to) + x <= vr) { inc(root, 1, n, nl, nr, x); return; } while (nr >= nl) { clarify(root, 1, n, nr); if (calc(nr, val[nr].from) + x >= vr) nr--; else { int l = val[nr].from, r = val[nr].to + 1; while (l < r) { int mid = (l + r) / 2; if (calc(nr, mid) + x <= vr) l = mid + 1; else r = mid; } val[nr].to = l - 1; inc(root, 1, n, nl, nr, x); clarify(root, 1, n, ++nr); val[nr] = (Line) {l, vr, vr, 0}; return; } } clarify(root, 1, n, ++nr); val[nr] = (Line) {vl, vr, vr, 0}; } void dec(int x) { clarify(root, 1, n, nl); if (calc(nl, val[nl].from) - x >= vl) { inc(root, 1, n, nl, nr, -x); return; } while (nr >= nl) { clarify(root, 1, n, nl); if (calc(nl, val[nl].to) - x <= vl) nl++; else { int l = val[nl].from - 1, r = val[nl].to; while (l < r) { int mid = (l + r + 1) / 2; if (calc(nl, mid) - x >= vl) r = mid - 1; else l = mid; } val[nl].index = calc(nl, l + 1); val[nl].from = l + 1; inc(root, 1, n, nl, nr, -x); clarify(root, 1, n, --nl); val[nl] = (Line) {vl, l, vl, 0}; return; } } clarify(root, 1, n, --nl); val[nl] = (Line) {vl, vr, vl, 0}; } void times(int x) { clarify(root, 1, n, nr); if (calc(nr, val[nr].to) * x <= vr) { times(root, 1, n, nl, nr, x); return; } while (nr >= nl) { clarify(root, 1, n, nr); if (calc(nr, val[nr].from) * x >= vr) nr--; else { int l = val[nr].from, r = val[nr].to + 1; while (l < r) { int mid = (l + r) / 2; if (calc(nr, mid) * x <= vr) l = mid + 1; else r = mid; } val[nr].to = l - 1; times(root, 1, n, nl, nr, x); clarify(root, 1, n, ++nr); val[nr] = (Line) {l, vr, vr, 0}; return; } } clarify(root, 1, n, ++nr); val[nr] = (Line) {vl, vr, vr, 0}; } void modify(long long x) { clarify(root, 1, n, nr); if (calc(nr, val[nr].to) + val[nr].to * x <= vr) { times(root, 1, n, nl, nr, x); return; } while (nr >= nl) { clarify(root, 1, n, nr); if (calc(nr, val[nr].from) + val[nr].from * x >= vr) nr--; else { int l = val[nr].from, r = val[nr].to + 1; while (l < r) { int mid = (l + r) / 2; if (calc(nr, mid) + mid * x <= vr) l = mid + 1; else r = mid; } val[nr].to = l - 1; modify(root, 1, n, nl, nr, x); clarify(root, 1, n, ++nr); val[nr] = (Line) {l, vr, vr, 0}; return; } } clarify(root, 1, n, ++nr); val[nr] = (Line) {vl, vr, vr, 0}; } void finish() { for (int i = nl; i <= nr; i++) clarify(root, 1, n, i); } int query(int x) { int l = nl, r = nr; while (true) { int mid = (l + r) / 2; if (val[mid].from <= x && val[mid].to >= x) return calc(mid, x); if (val[mid].from <= x) l = mid + 1; else r = mid - 1; } } } ST; int n, l, r, q; int main() { read(n), read(l), read(r); ST.init(n, l, r); for (int i = 1; i <= n; i++) { char opt; int x; getopt(opt), read(x); if (opt == '+') ST.inc(x); if (opt == '-') ST.dec(x); if (opt == '*') ST.times(x); if (opt == '@') ST.modify(x); } ST.finish(); read(q); for (int i = 1; i <= q; i++) { int x; read(x); writeln(ST.query(x)); } return 0; }