【LOJ3082】「2019 集训队互测 Day 5」小水题

这篇博客介绍了LOJ3082问题的解决策略,主要涉及钻孔操作对水位影响的分析。通过定义querySum、queryLeft和modifyLeft三个函数来描述水位变化,并利用李超式线段树实现O(NLog^2N + QLog^3N)的时间复杂度。通过优化二分查找,将时间复杂度降低到O(NLog^2N + QLog^2N)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】

【思路要点】

  • 考虑一次钻孔操作对全局水位的影响。
  • 注意到一次钻孔操作只会使得水向某一方向流动,以下认为水向右流动进行讨论。
  • 定义函数 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值