LOJ#3082「2019 集训队互测 Day 5」小水题(线段树)
Solution
考虑一次操作 2 x h
会发生什么。
首先可以明确一点,每次修改操作过后,水要么只会往右流,要么只会往左流,要么水位不会有任何改变。
这里考虑只往右流的情况:
\(a\) 是水位,\(b\) 是隔板高度,操作 2 x h
看作 \(b[x]=min(b[x],h)\)。
往右流的条件:\(a[x]>max(a[x+1],h)\)。
我们发现,当水从 \(x\) 流向 \(x+1\) 时,\(a[x]\) 会减少,此时可能导致 \(a[x-1]>max(b[x-1],a[x])\),也就是导致 \(x-1\) 的水流向 \(x\),以此类推,我们可以找到本次修改影响范围的左端点 \(l\)。
\(l\) 需要满足条件:\(a[l]\sim a[x]\) 全部相等,且 \(b[l]\sim b[x-1]\) 都小于 \(a[x]\)。
接下来要找到影响范围的右端点 \(r\)。
考虑 \(r\) 需要满足什么条件。我们判断 \(r\) 是否会被影响到的时候,可以假装 \(b[l-1]\) 和 \(b[r]\) 都是无限大。
我们先算出 \(a[l]\sim a[r]\) 在本次修改之前共有多少水,记为 \(sum\)。
接下来,我们将 \(l\sim r\) 的水都抽干,然后算出 \(a[l]\sim a[r]\) 都取后缀最大值时,需要多少水,记为 \(val\)。
后缀最大值的定义:\(a[i]=\max(b[i]\sim b[r-1],a[r])\),此处 \(a[r]\) 是修改之前的 \(a[r]\)。
若 \(val\le sum\),则 \(r\) 会被影响到。
为什么呢?因为如果会影响到 \(r\),记修改之后的 \(a[r]=k\),那么有 \(\forall i\in[l,r],a[i]=\max(b[i]\sim b[r-1],k)\)。
记 \(c[i]=\max(b[i]\sim b[r-1],k)\),首先水流静止状态下,不会有 \(c[i]>b[i]\)。如果存在 \(i<r,c[i]<b[i]\),那么前面的水就会流到 \(i\) 里面去,不会流到 \(r\)。
找到 \(r\) 之后,我们让 \(a[r]\) 还是修改之前的水量,然后把 \(a[l-1]\sim a[r-1]\) 都填成后缀最大值。
然后我们要把剩下的 \(sum-val\) 水量填入 \(l\sim r\)。也就是说,我们要找到一个最小的 \(s\),满足条件:$$\max(b[s]\sim b[r-1],a[r])×(r-s+1)-(\sum_{i=s}^ra[i])\le sum-val$$
即把 \(s\sim r\) 都填成:$$\frac{(\sum_{i=s}^ra[i])+sum-val}{r-s+1}$$
暴力实现的时间复杂度为 \(O(n^2\log n)\)。
考虑用线段树维护上述信息:
找 \(l\) 需要存下 \(A\) 的区间最大,最小值,\(B\) 的区间最值,然后直接在线段树上二分。
找 \(r\):记 \(query(l,r)\) 表示把 \(a[l]\sim a[r]\) 都填成后缀最大值,需要多少水。
则 \(r\) 满足条件:\(sum(l,r)\ge query(l,r)\)。
这要怎么在线段树上二分呢?
如图,假设我们现在二分到了 \(r4\),那么 \(D,E,G,F\) 是 \([l,r4]\) 的在原线段树上拆成的边界区间,\(A,B,C\) 是新建节点。
我们在二分的同时,要进行删点和加点操作。
这样就能在 \(O(\log n)\) 的时间内查询 \(query(l,r4)\) 的值(类似线段树维护单调上升子序列),在 \(O(1)\) 的时间查询 \(sum(l,r4)\) 的值。
而新建节点组成的树高显然是 \(O(\log n)\) 的,更新一个节点信息也只要 \(O(\log n)\)。
再加上线段树上二分,总时间复杂度 \(O(\log^2 n)\)。
将 \(a[l]\sim a[r]\) 都改成后缀最大值,只要给线段树上的 \(O(\log n)\) 个节点打上标记即可。
找 \(s\) 只要开一个全局变量,假设此时二分到了 \(s1\),我们要维护 \(\max(b[s1]\sim b[r-1],a[r])\)。
总时间复杂度 \(O(n\log^2 n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define ld long double
const int e = 4e5 + 5;
const ld eps = 1e-12;
struct node
{
int lc, rc, l, r, len, tag_op;
ld sum, maxA, minA, tag_v, sumL, sumR, maxB, wl, wr;
}c[e];
ld a[e], b[e], now_c, ret, sum;
int n, q, pool, lst_p, now_l, now_r, t2;
inline ld askL(int x, ld cur)
{
if (cur - c[x].maxB >= -eps) return c[x].len * cur;
int lc = c[x].lc, rc = c[x].rc;
ld t = b[c[lc].r];
if (cur - c[lc].maxB >= -eps) return c[lc].len * cur + askL(rc, max(cur, t));
return c[x].sumR + askL(lc, cur);
}
inline ld askR(int x, ld cur)
{
if (cur - c[x].maxB >= -eps) return c[x].len * cur;
int lc = c[x].lc, rc = c[x].rc;
ld t = b[c[lc].r];
if (cur - c[rc].maxB >= -eps) return c[rc].len * cur + askR(lc, max(cur, t));
return c[x].sumL + askR(rc, cur);
}
inline void upt(int x, bool op)
{
int lc = c[x].lc, rc = c[x].rc;
ld t = b[c[lc].r];
c[x].sum = c[lc].sum + c[rc].sum;
c[x].maxA = max(c[lc].maxA, c[rc].maxA);
c[x].minA = min(c[lc].minA, c[rc].minA);
c[x].maxB = max(c[lc].maxB, max(c[rc].maxB, t));
c[x].wl = c[lc].wl;
c[x].wr = c[rc].wr;
if (op)
{
c[x].sumL = askR(lc, max(c[rc].maxB, t));
c[x].sumR = askL(rc, max(c[lc].maxB, t));
}
}
inline void build(int l, int r, int &x)
{
x = ++pool;
c[x].l = l; c[x].r = r; c[x].len = r - l + 1;
if (l == r)
{
c[x].sum = c[x].maxA = c[x].minA = a[l];
c[x].wl = c[x].wr = a[l];
return;
}
int mid = l + r >> 1;
build(l, mid, c[x].lc);
build(mid + 1, r, c[x].rc);
upt(x, 1);
}
inline void addtag(int x, int op, ld v)
{
int lc = c[x].lc, rc = c[x].rc;
c[x].tag_op = op;
c[x].tag_v = v;
c[x].minA = v;
c[x].maxA = max(v, c[x].maxB);
if (op == 1)
{
c[x].sum = askL(x, v);
c[x].wl = v;
c[x].wr = max(c[x].maxB, v);
}
else
{
c[x].sum = askR(x, v);
c[x].wr = v;
c[x].wl = max(c[x].maxB, v);
}
}
inline void pushdown(int x)
{
if (c[x].tag_op)
{
int lc = c[x].lc, rc = c[x].rc, op = c[x].tag_op;
ld v = c[x].tag_v, t = b[c[lc].r];
if (op == 1)
{
addtag(lc, 1, v);
addtag(rc, 1, max(c[lc].maxB, max(t, v)));
}
else
{
addtag(rc, 2, v);
addtag(lc, 2, max(c[rc].maxB, max(t, v)));
}
c[x].tag_op = c[x].tag_v = 0;
}
}
inline ld asksum(int l, int r, int s, int t, int x)
{
if (l == s && r == t) return c[x].sum;
pushdown(x);
int mid = l + r >> 1;
if (t <= mid) return asksum(l, mid, s, t, c[x].lc);
else if (s > mid) return asksum(mid + 1, r, s, t, c[x].rc);
else return asksum(l, mid, s, mid, c[x].lc) + asksum(mid + 1, r, mid + 1, t, c[x].rc);
}
inline bool check_L(int x, ld tmp)
{
return fabs(c[x].maxA - c[x].minA) <= eps && fabs(c[x].maxA - tmp) <= eps
&& c[x].maxA - max(c[x].maxB, b[c[x].r]) >= eps;
}
inline int find_L1(int l, int r, int ed, ld v, int x)
{
if (l == r)
{
if (check_L(x, v)) return l;
else return l + 1;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (ed <= mid) return find_L1(l, mid, ed, v, lc);
else if (r <= ed)
{
if (check_L(x, v)) return l;
if (check_L(rc, v)) return find_L1(l, mid, ed, v, lc);
else return find_L1(mid + 1, r, ed, v, rc);
}
else
{
int x = find_L1(mid + 1, r, ed, v, rc);
if (x != mid + 1) return x;
else return find_L1(l, mid, ed, v, lc);
}
}
inline bool check_R(int x, ld tmp)
{
return fabs(c[x].maxA - c[x].minA) <= eps && fabs(c[x].maxA - tmp) <= eps
&& c[x].maxA - max(c[x].maxB, b[c[x].l - 1]) >= eps;
}
inline int find_R1(int l, int r, int st, ld v, int x)
{
if (l == r)
{
if (check_R(x, v)) return l;
else return l - 1;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (mid < st) return find_R1(mid + 1, r, st, v, rc);
else if (l >= st)
{
if (check_R(x, v)) return r;
if (check_R(lc, v)) return find_R1(mid + 1, r, st, v, rc);
else return find_R1(l, mid, st, v, lc);
}
else
{
int x = find_R1(l, mid, st, v, lc);
if (x != mid) return x;
else return find_R1(mid + 1, r, st, v, rc);
}
}
inline void collect(int x, int lc, int rc)
{
c[x].lc = lc; c[x].rc = rc;
c[x].l = c[lc].l; c[x].r = c[rc].r;
c[x].len = c[x].r - c[x].l + 1;
c[x].tag_op = c[x].tag_v = 0;
upt(x, 1);
}
inline void add_R(int x)
{
pool++;
if (pool == lst_p + 1) c[pool] = c[x];
else collect(pool, pool - 1, x);
}
inline bool pd_R(int x)
{
add_R(x);
ld ar = c[pool].wr, sum = c[pool].sum, val = askR(pool, ar);
return fabs(sum - val) <= eps || sum - val >= eps;
}
inline int se_R(int l, int r, int st, int x)
{
if (l == r)
{
if (pd_R(x)) return l;
else return l - 1;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (mid < st) return se_R(mid + 1, r, st, rc);
else if (l >= st)
{
if (pd_R(x)) return r;
pool--;
if (pd_R(lc)) return se_R(mid + 1, r, st, rc);
else
{
pool--;
return se_R(l, mid, st, lc);
}
}
else
{
int x = se_R(l, mid, st, lc);
if (x != mid) return x;
else return se_R(mid + 1, r, st, rc);
}
}
inline int find_R2(int st)
{
lst_p = pool;
int res = se_R(1, n, st, 1);
pool = lst_p;
return res;
}
inline void add_L(int x)
{
pool++;
if (pool == lst_p + 1) c[pool] = c[x];
else collect(pool, x, pool - 1);
}
inline bool pd_L(int x)
{
add_L(x);
ld al = c[pool].wl, sum = c[pool].sum, val = askL(pool, al);
return fabs(sum - val) <= eps || sum - val >= eps;
}
inline int se_L(int l, int r, int ed, int x)
{
if (l == r)
{
if (pd_L(x)) return l;
else return l + 1;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (ed <= mid) return se_L(l, mid, ed, lc);
else if (r <= ed)
{
if (pd_L(x)) return l;
pool--;
if (pd_L(rc)) return se_L(l, mid, ed, lc);
else
{
pool--;
return se_L(mid + 1, r, ed, rc);
}
}
else
{
int x = se_L(mid + 1, r, ed, rc);
if (x != mid + 1) return x;
else return se_L(l, mid, ed, lc);
}
}
inline int find_L2(int ed)
{
lst_p = pool;
int res = se_L(1, n, ed, 1);
pool = lst_p;
return res;
}
inline void uptB(int l, int r, int s, ld v, int x)
{
if (l == r)
{
b[l] = v;
return;
}
pushdown(x);
int mid = l + r >> 1;
if (s <= mid) uptB(l, mid, s, v, c[x].lc);
else uptB(mid + 1, r, s, v, c[x].rc);
upt(x, 1);
}
inline void modify_R(int l, int r, int s, int t, int x)
{
if (l == s && r == t)
{
if (r != now_r) now_c = max(now_c, b[r]);
addtag(x, 2, now_c);
now_c = max(now_c, c[x].maxB);
return;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (t <= mid) modify_R(l, mid, s, t, lc);
else if (s > mid) modify_R(mid + 1, r, s, t, rc);
else modify_R(mid + 1, r, mid + 1, t, rc), modify_R(l, mid, s, mid, lc);
upt(x, 0);
}
inline void modify_L(int l, int r, int s, int t, int x)
{
if (l == s && r == t)
{
if (l != now_l) now_c = max(now_c, b[l - 1]);
addtag(x, 1, now_c);
now_c = max(now_c, c[x].maxB);
return;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (t <= mid) modify_L(l, mid, s, t, lc);
else if (s > mid) modify_L(mid + 1, r, s, t, rc);
else modify_L(l, mid, s, mid, lc), modify_L(mid + 1, r, mid + 1, t, rc);
upt(x, 0);
}
inline int query_L(int l, int r, int st, int ed, int x)
{
if (l == r)
{
now_c = max(now_c, c[x].maxA);
sum += c[x].maxA;
if (now_c * (ed - l + 1) - sum - ret <= eps) return l;
else return l + 1;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (ed <= mid) return query_L(l, mid, st, ed, lc);
else if (mid < st) return query_L(mid + 1, r, st, ed, rc);
else if (st <= l && r <= ed)
{
if (max(now_c, c[x].maxA) * (ed - l + 1) - c[x].sum - sum - ret <= eps)
{
now_c = max(now_c, c[x].maxA);
sum += c[x].sum;
return l;
}
if (max(now_c, c[rc].maxA) * (ed - mid) - c[rc].sum - sum - ret <= eps)
{
now_c = max(now_c, c[rc].maxA);
sum += c[rc].sum;
return query_L(l, mid, st, ed, lc);
}
else return query_L(mid + 1, r, st, ed, rc);
}
else
{
int x = query_L(mid + 1, r, st, ed, rc);
if (x != mid + 1) return x;
return query_L(l, mid, st, ed, lc);
}
}
inline int query_R(int l, int r, int st, int ed, int x)
{
if (l == r)
{
now_c = max(now_c, c[x].maxA);
sum += c[x].sum;
if (now_c * (r - st + 1) - sum - ret <= eps) return l;
else return l - 1;
}
pushdown(x);
int mid = l + r >> 1, lc = c[x].lc, rc = c[x].rc;
if (mid < st) return query_R(mid + 1, r, st, ed, rc);
else if (ed <= mid) return query_R(l, mid, st, ed, lc);
else if (st <= l && r <= ed)
{
if (max(now_c, c[x].maxA) * (r - st + 1) - c[x].sum - sum - ret <= eps)
{
now_c = max(now_c, c[x].maxA);
sum += c[x].sum;
return r;
}
if (max(now_c, c[lc].maxA) * (mid - st + 1) - c[lc].sum - sum - ret <= eps)
{
now_c = max(now_c, c[lc].maxA);
sum += c[lc].sum;
return query_R(mid + 1, r, st, ed, rc);
}
else return query_R(l, mid, st, ed, lc);
}
else
{
int x = query_R(l, mid, st, ed, lc);
if (x != mid) return x;
return query_R(mid + 1, r, st, ed, rc);
}
}
int main()
{
scanf("%d%d", &n, &q);
int i, x, pos, op, l, r, st, ed, tim = 0; double t; ld h;
for (i = 1; i <= n; i++) scanf("%lf", &t), a[i] = t;
for (i = 1; i < n; i++) scanf("%lf", &t), b[i] = t;
build(1, n, x);
while (q--)
{
scanf("%d%d", &op, &pos);
if (op == 1)
{
scanf("%lf", &t); h = t;
if (b[pos] - h <= eps) continue;
uptB(1, n, pos, h, 1);
ld a1 = asksum(1, n, pos, pos, 1), a2 = asksum(1, n, pos + 1, pos + 1, 1);
if (a1 - h >= eps && a1 - a2 >= eps)
{
l = find_L1(1, n, pos, a1, 1);
r = find_R2(l);
ld ar = asksum(1, n, r, r, 1), lst_s = asksum(1, n, l, r, 1);
now_c = ar; now_r = r;
modify_R(1, n, l, r, 1);
now_c = ar;
ret = lst_s - asksum(1, n, l, r, 1); sum = 0;
st = query_L(1, n, l, r, 1);
st = max(st, l);
ld all = asksum(1, n, st, r, 1) + ret;
now_c = all / (r - st + 1);
modify_R(1, n, st, r, 1);
}
else if (a2 - h >= eps && a2 - a1 >= eps)
{
r = find_R1(1, n, pos + 1, a2, 1);
l = find_L2(r);
ld al = asksum(1, n, l, l, 1), lst_s = asksum(1, n, l, r, 1);
now_c = al; now_l = l;
modify_L(1, n, l, r, 1);
now_c = al;
ret = lst_s - asksum(1, n, l, r, 1); sum = 0;
ed = query_R(1, n, l, r, 1);
ld all = asksum(1, n, l, ed, 1) + ret;
now_c = all / (ed - l + 1);
modify_L(1, n, l, ed, 1);
}
}
else
{
double ans = asksum(1, n, pos, pos, 1);
printf("%.8lf\n", ans);
}
}
for (i = 1; i <= n; i++)
{
double ans = asksum(1, n, i, i, 1);
printf("%.8lf ", ans);
}
return 0;
}