P9130 [USACO23FEB] Hungry Cow P (动态开点线段树+线段树二分)
P9130 [USACO23FEB] Hungry Cow P
动态开点线段树+线段树二分
考虑线段树。
首先区间范围是 \([1,10^{14}]\),而需要的点只有 \(n\log n\) 个,所以需要用到动态开点线段树。
考虑合并,需要维护每个区间留下的稻草 \(left\) 和还没填的空的数量 \(blank\) 以及 \(ans\)。
对于 \(left\) 和 \(blank\) 的合并,只有左区间的 \(left\) 可以贡献右区间,只有右区间的 \(blank\) 可以被消掉,所以写出来
\(u_{left}=rs_{left}+\max(0,ls_{left}-rs_{blank})\)
\(u_{blank}=ls_{blank}+\max(0,rs_{blank}-ls_{left})\)
对于 \(ans\) 无法做到 \(O(1)\) 维护,这时候可以选择维护更多的信息或者线段树上二分。考虑后者。
首先 \(ls_{ans}\) 肯定是答案的一部分,接下来就是分讨右区间,考虑构造一个函数 \(calc(l,r,x)\) 表示当前在 \([l,r]\) 前面留下 \(x\) 个稻草,所以此时左区间贡献给右区间 \(ls_{left}\)。
若 \(x\le rls.blank\),那么 \(rrs\) 的答案只会被 \(rls\) 影响,就是 \(rs_{ans}-rls_{ans}\),并递归 \(calc(l,mid,x)\)。
否则,\(rls\) 的区间被填满,答案可以 \(O(1)\) 算出,并递归 \(calc(mid+1,r,x-rls_{blank}+rls_{left})\)。
每次只会递归一半,所以 \(calc\) 的复杂度是 \(O(\log n)\),总复杂度是 \(O(n\log^2 n)\)。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
typedef long long i64;
const int N = 10000010, mod = 1000000007, inv2 = 500000004;
i64 tot = 1;
struct seg {
i64 ls, rs;
i64 left, blank, ans;
} t[N];
void crea(i64 &u, i64 l, i64 r) {
if(u) return;
u = ++tot;
t[u].blank = r - l + 1;
t[u].left = 0;
t[u].ans = 0;
}
void pushup(i64 u, i64 l, i64 r) {
i64 mid = (l + r) >> 1;
t[u].left = std::max(0ll, std::max(0ll, t[t[u].ls].left - std::min(r - mid, t[t[u].rs].blank)) + t[t[u].rs].left);
t[u].blank = std::max(0ll, std::min(mid - l + 1, t[t[u].ls].blank) + std::max(0ll, std::min(r - mid, t[t[u].rs].blank) - t[t[u].ls].left));
}
i64 query(i64 &u, i64 l, i64 r, i64 x) {
if(l == r) return (x ? l % mod : t[u].ans);
i64 mid = (l + r) >> 1;
if(x <= std::min(mid - l + 1, t[t[u].ls].blank)) return (query(t[u].ls, l, mid, x) + (t[u].ans - t[t[u].ls].ans + mod) % mod) % mod;
return (((l + mid) % mod * ((mid - l + 1) % mod) % mod * inv2 % mod) + query(t[u].rs, mid + 1, r, x - std::min(mid - l + 1, t[t[u].ls].blank) + t[t[u].ls].left)) % mod;
}
void update(i64 &u, i64 l, i64 r, i64 x, i64 y) {
crea(u, l, r);
if(l == r) {
if(!y) t[u].left = 0, t[u].blank = 1, t[u].ans = 0;
else t[u].left = y - 1, t[u].blank = 0, t[u].ans = l % mod;
return;
}
i64 mid = (l + r) >> 1;
if(x <= mid) update(t[u].ls, l, mid, x, y);
else update(t[u].rs, mid + 1, r, x, y);
pushup(u, l, r);
t[u].ans = (t[t[u].ls].ans + query(t[u].rs, mid + 1, r, t[t[u].ls].left)) % mod;
}
i64 n, rt = 1;
void Solve() {
std::cin >> n;
t[1].blank = t[0].blank = 2e14;
for(int i = 1; i <= n; i++) {
i64 a, b;
std::cin >> a >> b;
update(rt, 1, 2e14, a, b);
std::cout << t[rt].ans << "\n";
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
Solve();
return 0;
}