【题目链接】
【思路要点】
- 考虑一次钻孔操作对全局水位的影响。
- 注意到一次钻孔操作只会使得水向某一方向流动,以下认为水向右流动进行讨论。
- 定义函数 q u e r y S u m ( l , r ) querySum(l,r) querySum(l,r) 表示区间 [ l , r ] [l,r] [l,r] 的水位和。
- 定义函数 q u e r y L e f t ( l , r , h ) queryLeft(l,r,h) queryLeft(l,r,h) 表示假设区间 [ l , r ] [l,r] [l,r] 两侧的挡板高度无限,在区间中没有水的时候需要在 l l l 处倒入多少水使得 r r r 处的水位到达 h h h 。
- 定义操作 m o d i f y L e f t ( l , r , h ) modifyLeft(l,r,h) modifyLeft(l,r,h) 表示假设区间 [ l , r ] [l,r] [l,r] 两侧的挡板高度无限,抽空区间中的水,并在 l l l 处倒入一定量的水,直到 r r r 处的水位到达 h h h 。
- 假设一次钻孔操作 2 p o s h 2\ pos\ h 2 pos h 使得 p o s pos pos 处的水将要流向 p o s + 1 pos+1 pos+1 。
- 首先,找到最小的 l l l ,使得 [ l , p o s ] [l,pos] [l,pos] 内的水位相等,且挡板高度均不足水位高度。
- 计算 v o l = q u e r y S u m ( l , p o s ) − q u e r y L e f t ( l , p o s , h ) vol=querySum(l,pos)-queryLeft(l,pos,h) vol=querySum(l,pos)−queryLeft(l,pos,h) ,即最多可以流出的水量,并执行操作 m o d i f y L e f t ( l , p o s , h ) modifyLeft(l,pos,h) modifyLeft(l,pos,h) 。
- 其次,找到最大的 r r r ,使得 q u e r y L e f t ( l , r , a r ) − q u e r y S u m ( l , r ) ≤ v o l queryLeft(l,r,a_r)-querySum(l,r)\leq vol queryLeft(l,r,ar)−querySum(l,r)≤vol ,即此次流动将会影响到的区间右端点。
- 最后,找到最大的 v a l val val ,使得 q u e r y L e f t ( l , r , v a l ) − q u e r y S u m ( l , r ) ≤ v o l queryLeft(l,r,val)-querySum(l,r)\leq vol queryLeft(l,r,val)−querySum(l,r)≤vol ,即最后 r r r 处的水位,执行操作 m o d i f y L e f t ( l , p o s , v a l ) modifyLeft(l,pos,val) modifyLeft(l,pos,val) 即可。
- 使用李超式线段树可以在 O ( L o g 2 N ) O(Log^2N) O(Log2N) 的时间内实现 q u e r y S u m querySum querySum q u e r y L e f t queryLeft queryLeft m o d i f y L e f t modifyLeft modifyLeft 中的每一个功能,直接二分找到 l , r , v o l l,r,vol l,r,vol 可以得到 O ( N L o g 2 N + Q L o g 3 N ) O(NLog^2N+QLog^3N) O(NLog2N+QLog3N) 的时间复杂度。
- 将二分改为在线段树上二分,时间复杂度降为 O ( N L o g 2 N + Q L o g 2 N ) O(NLog^2N+QLog^2N) O(NLog2N+QLog2N) 。
【代码】
// Long Double Version Till Line 439 #include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const long double eps = 1e-12; typedef long long ll; typedef long double ld; 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(""); } struct SegmentTree { struct Node { int lc, rc, l, r, len, tagtype; // 0 : Nothing, 1 : FloodLeft, 2 : FloodRight ld Sum, Max, Min, Maxh, WaterL, WaterR, Tag, VolLeft, VolRight; } a[MAXN * 2]; ld water[MAXN], block[MAXN]; int root, size, n; ld res, cur; bool vis; ld getvolLeft(int root, ld cur) { if (a[root].len == 1) return cur; if (cur > max(a[a[root].rc].Maxh, block[a[a[root].lc].r])) return cur * a[a[root].rc].len + getvolLeft(a[root].lc, cur); else return a[root].VolLeft + getvolLeft(a[root].rc, cur); } ld getvolRight(int root, ld cur) { if (a[root].len == 1) return cur; if (cur > max(a[a[root].lc].Maxh, block[a[a[root].lc].r])) return cur * a[a[root].lc].len + getvolRight(a[root].rc, cur); else return a[root].VolRight + getvolRight(a[root].lc, cur); } void update(int root, bool type) { a[root].Sum = a[a[root].lc].Sum + a[a[root].rc].Sum; a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max); a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min); a[root].WaterL = a[a[root].lc].WaterL; a[root].WaterR = a[a[root].rc].WaterR; if (type) { a[root].Maxh = max(a[a[root].lc].Maxh, a[a[root].rc].Maxh); chkmax(a[root].Maxh, block[a[a[root].lc].r]); a[root].VolLeft = getvolLeft(a[root].lc, max(a[a[root].rc].Maxh, block[a[a[root].lc].r])); a[root].VolRight = getvolRight(a[root].rc, max(a[a[root].lc].Maxh, block[a[a[root].lc].r])); } } void puttag(int root, ld tag, int type) { a[root].Tag = tag; a[root].tagtype = type; a[root].Min = tag; a[root].Max = max(tag, a[root].Maxh); if (type == 1) { a[root].WaterR = tag; a[root].WaterL = max(tag, a[root].Maxh); a[root].Sum = getvolLeft(root, tag); } else { a[root].WaterL = tag; a[root].WaterR = max(tag, a[root].Maxh); a[root].Sum = getvolRight(root, tag); } } void pushdown(int root) { if (a[root].tagtype == 1) { puttag(a[root].rc, a[root].Tag, 1); puttag(a[root].lc, max(a[root].Tag, max(a[a[root].rc].Maxh, block[a[a[root].lc].r])), 1); a[root].tagtype = 0; } if (a[root].tagtype == 2) { puttag(a[root].lc, a[root].Tag, 2); puttag(a[root].rc, max(a[root].Tag, max(a[a[root].lc].Maxh, block[a[a[root].lc].r])), 2); a[root].tagtype = 0; } } void build(int &root, int l, int r) { root = ++size; a[root].l = l; a[root].r = r; a[root].len = r - l + 1; if (l == r) { a[root].Sum = water[l]; a[root].Max = water[l]; a[root].Min = water[l]; a[root].WaterL = water[l]; a[root].WaterR = water[l]; a[root].Maxh = 0; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root, true); } void init(int x) { n = x; root = size = 0; for (int i = 1; i <= n; i++) scanf("%Lf", &water[i]); for (int i = 1; i <= n - 1; i++) scanf("%Lf", &block[i]); build(root, 1, n); } ld querySum(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Sum; pushdown(root); int mid = (l + r) / 2; ld ans = 0; if (mid >= ql) ans += querySum(a[root].lc, l, mid, ql, min(mid, qr)); if (mid + 1 <= qr) ans += querySum(a[root].rc, mid + 1, r, max(mid + 1, ql), qr); return ans; } ld querySum(int l, int r) { return querySum(root, 1, n, l, r); } void modifyLeft(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr