Loading

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;
}
posted @ 2024-03-27 21:00  Fire_Raku  阅读(37)  评论(0编辑  收藏  举报