P4192 旅行规划 题解

\(\text{P4192 旅行规划 题解}\)

还有点意思的题。

题目等价于区间加等差数列,求前缀最大和。这个东西线段树之类的数据结构显然是不好维护的。看到时限 4s,考虑分块,块间的情况是容易处理的,现在考虑如何快速求出块内的最大权值点。

由于是等差数列,那么我们可以轻易知道每个命令块内两个点贡献的差值。对于一个块内两个点 \(j<i\),我们设 \(b_i\) 表示点 \(i\) 原先的贡献,\(\Delta\) 表示在这个块之前加入的总贡献,那么 \(i\) 优于 \(j\) 的条件是:\(b_i+i\times \Delta>b_j+j\times \Delta\),转化一下得到的是:

\[\frac{b_i-b_j}{i-j}>-\Delta \]

那么块内维护一下凸包就行了。时间复杂度是 \(O(n\sqrt n\log n)\)

代码写得有些复杂了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5, M = 350, inf = 1e18;
int n, m, T, b[N];
int bel[N];
struct PNT {
	int x, y;
	bool operator < (const PNT &b) const {
		if (x == b.x) return y < b.y;
		return x < b.x;
	}
	PNT operator - (PNT b) {
		return {x - b.x, y - b.y};
	}
	int operator * (PNT b) {
		return x * b.y - y * b.x;
	}
};
#define pii pair<int, int>
#define mk make_pair
struct Node {
	int l, r, dlt, adt;
	int b[M];
	vector<pii>va;
	vector<PNT>v;
} e[M];
PNT stk[N];
int top;
void fuck(vector<PNT>&v) {
	top = 0;
	sort(v.begin(), v.end());
	for (auto i : v) {
		while (top > 1 && (i - stk[top]) * (stk[top] - stk[top - 1]) <= 0) --top;
		stk[++top] = i;
	}
	v.clear();
	for (int i = 1; i <= top; i++) v.push_back(stk[i]);
}

void update(int x, int y, int k) {
	int bl = bel[x], br = bel[y];
	for (int i = bl + 1; i < br; i++) {
		e[i].va.emplace_back(k, x);
		e[i].dlt += k;
		e[i].adt += (e[i].l - x) * k;
	}
	
	for (int i = e[bl].l; i <= e[bl].r; i++) {
		e[bl].b[i - e[bl].l + 1] += e[bl].adt; 
		for (auto j : e[bl].va)
			e[bl].b[i - e[bl].l + 1] += j.first * (i - e[bl].l + 1);
		if (x <= i && i <= y) e[bl].b[i - e[bl].l + 1] += k * (i - x + 1);
	}
	e[bl].dlt = e[bl].adt = 0, e[bl].va.clear(), e[bl].v.clear();
	for (int i = e[bl].l; i <= e[bl].r; i++)
		e[bl].v.emplace_back((PNT){i, e[bl].b[i - e[bl].l + 1]});
	fuck(e[bl].v);
	if (bl == br) return; 
	
	for (int i = e[br].l; i <= e[br].r; i++) {
		e[br].b[i - e[br].l + 1] += e[br].adt; 
		for (auto j : e[br].va)
			e[br].b[i - e[br].l + 1] += j.first * (i - e[br].l + 1);
		if (x <= i && i <= y) e[br].b[i - e[br].l + 1] += k * (i - x + 1);
	}
	e[br].dlt = e[br].adt = 0, e[br].va.clear(), e[br].v.clear();
	for (int i = e[br].l; i <= e[br].r; i++)
		e[br].v.emplace_back((PNT){i, e[br].b[i - e[br].l + 1]});
	fuck(e[br].v);
}
void rebut(int x, int k) {
	if (x > n) return;
	int bl = bel[x];
	for (int i = bl + 1; i <= m; i++) e[i].adt += k;
	for (int i = e[bl].l; i <= e[bl].r; i++) {
		e[bl].b[i - e[bl].l + 1] += e[bl].adt;
		for (auto j : e[bl].va)
			e[bl].b[i - e[bl].l + 1] += j.first * (i - e[bl].l + 1);
		if (x <= i) e[bl].b[i - e[bl].l + 1] += k;
	}
	e[bl].dlt = e[bl].adt = 0, e[bl].va.clear(), e[bl].v.clear();
	for (int i = e[bl].l; i <= e[bl].r; i++)
		e[bl].v.emplace_back((PNT){i, e[bl].b[i - e[bl].l + 1]});
	fuck(e[bl].v);
}
int query(int x, int y) {
	int res = -inf, bl = bel[x], br = bel[y];
	for (int i = bl + 1; i < br; i++) {
		int l = 1, r = e[i].v.size() - 1, ans = 0, mid = 0;
		while (l <= r) {
			mid = (l + r) >> 1;
			if (e[i].v[mid].y - e[i].v[mid - 1].y >= -e[i].dlt * (e[i].v[mid].x - e[i].v[mid - 1].x))
				ans = mid, l = mid + 1;
			else r = mid - 1;
		}
		ans = e[i].v[ans].x;
		res = max(res, e[i].b[ans - e[i].l + 1] + e[i].dlt * (ans - e[i].l + 1) + e[i].adt);
	}
	for (int i = e[bl].l; i <= e[bl].r; i++)
		if (x <= i && i <= y)
			res = max(res, e[bl].b[i - e[bl].l + 1] + e[bl].dlt * (i - e[bl].l + 1) + e[bl].adt);
	for (int i = e[br].l; i <= e[br].r; i++)
		if (x <= i && i <= y)
			res = max(res, e[br].b[i - e[br].l + 1] + e[br].dlt * (i - e[br].l + 1) + e[br].adt);
	return res;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	m = sqrt(n);
	for (int i = 1; i <= n; i++) cin >> b[i], b[i] += b[i - 1];
	for (int i = 1; i <= m; i++) {
		e[i].l = e[i - 1].r + 1;
		e[i].r = e[i].l + m - 1;
	}
	if (e[m].r < n) {
		++m;
		e[m].l = e[m - 1].r + 1;
		e[m].r = n;
	}
	for (int i = 1; i <= m; i++)
		for (int j = e[i].l; j <= e[i].r; j++)
			bel[j] = i;
	for (int i = 1; i <= m; i++) {
		for (int j = e[i].l; j <= e[i].r; j++) {
			e[i].b[j - e[i].l + 1] = b[j];
			e[i].v.emplace_back((PNT){j, b[j]});
		}
		fuck(e[i].v);
	}
	cin >> T;
	while (T--) {
		int op, x, y;
		cin >> op >> x >> y;
		if (!op) {
			int k;
			cin >> k;
			update(x, y, k);
			rebut(y + 1, (y - x + 1) * k);
		}
		else cout << query(x, y) << '\n';
	}
	return 0;
}
posted @ 2025-04-19 18:30  长安19路  阅读(9)  评论(0)    收藏  举报