洛谷 P4544 [USACO10NOV]Buying Feed G - 题解

https://www.luogu.com.cn/problem/P4544

感觉是很没意思的 DP + DS 优化,以前做过几道更难的题:

https://codeforces.com/contest/1788/problem/E

https://atcoder.jp/contests/abc288/tasks/abc288_f

这种题只要是让我写总是能把代码整的伤痕累累(逃

第一眼:我艹不就是一个 sb DP 吗,果断开写。

错误的状态设计:设 \(f_{i,j}\) 表示现在走到了坐标 \(X = i\),车上有 \(j\) 吨饲料的最小花费。

为什么错了?因为单个坐标有可能有多个商店。

正确的状态设计:先将商店坐标从小到大排序,然后设 \(f_{i, j}\) 表示现在考虑到第 \(i\) 个商店,车上有 \(j\) 吨饲料的最小花费。

我们不加结论证明的给出证明结论:

\[f_{i, j} = \min_{u = 0}^{\min(F_i, j)}f_{i - 1, j - u} + C_i \times u + (X_i - X_{i - 1}) \times (j - u)^2 \]

注意:\(f_{n, k}\) 并不是 答案,因为坐标只是 \(X_n\),真正的答案还要加上 \(X_n \sim E\) 的运费,即 \((E - X_n) \times k^2\)

srds,这是 \(O(nk F_i)\) 的,只能得到 \(85\) 分。

时间爆炸!如何优化?

考虑将 \(j - u\) 换元。

\(k' = j - u\),那么 \(j - \min (F_i, j) \leq k' \leq j\),令 \(D_i\)\(X_i - X_{i - 1}\),即与上一个商店的距离,可以得到新的转移方程:

\[f_{i, j} = \min_{k' = j - \min(F_i, j)}^{j}f_{i - 1, k'} + D_i \times {k'}^2 + C_i \times (j - k') = \min_{k' = j - \min(F_i, j)}^{j}f_{i - 1, k'} + D_i \times {k'}^2 + C_ij - C_ik' \]

然后发现式子的第一、二、四项都不受 \(j\) 的影响,这个可以用 DS 优化,就是预处理区间最小值,\(C_ij\) 这一项就是每次转移加上就行。

至于预处理,可以用线段树或者 ST 表,线段树不加 O2 \(95\) 分,加了 O2 可以过,ST 表不加 O2 \(100\) 分。

这个是 ST 表的代码:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
#define int long long
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline int read () {
	int x = 0, f = 0;
	char c = getchar ();
	for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
	for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
	return !f ? x : -x;
}
const int N = 505, M = 1e4 + 5;
int k, n, e, dp[N][M], mp[N], c[N];
struct store {
	int X, F, C;
	inline bool operator < (store p1) {
		return X < p1.X;
	}
} st[N];
int a[M], ST[M][15], lg[M];
inline void ST_pre () {
	int nm = k + 1;
	lg[0] = -1;
	for (int i = 1;i <= nm; ++ i) ST[i][0] = a[i], lg[i] = lg[i >> 1] + 1;
	for (int j = 1;j < 15; ++ j) {
		for (int i = 1;i + (1 << j) - 1 <= nm; ++ i) {
			ST[i][j] = min (ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
		}
	}
}
inline int query (int l, int r) {
	int k = lg[r - l + 1];
	return min (ST[l][k], ST[r - (1 << k) + 1][k]);
}
signed main () {
	k = read (), e = read () + 1, n = read ();
	for (int i = 0;i <= n; ++ i) {
		for (int j = 0;j <= k; ++ j) {
			dp[i][j] = 1e18;
		}
	}
	dp[0][0] = 0;
	for (int i = 1;i <= n; ++ i) st[i].X = read () + 1, st[i].F = read (), st[i].C = read ();
	sort (st + 1, st + 1 + n);
	for (int i = 1;i <= n; ++ i) {
		int D = st[i].X - st[i - 1].X;
		for (int j = 0;j <= k; ++ j) dp[i - 1][j] -= st[i].C * j - D * j * j, a[j + 1] = dp[i - 1][j];
		ST_pre ();
		for (int j = 0;j <= k; ++ j) {
			int L = j - min (st[i].F, j), R = j;
			dp[i][j] = min (dp[i][j], query (L + 1, R + 1) + j * st[i].C);
		}
	}
	printf ("%lld\n", dp[n][k] + (e - st[n].X) * k * k);
	return 0;
}
// Always keep it simple and stupid

这个是线段树的:

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
typedef long long ll;
typedef pair < int, int > PII;
typedef int itn;
mt19937 RND_MAKER (chrono :: steady_clock :: now ().time_since_epoch ().count ());
inline ll randomly (const ll l, const ll r) {return (RND_MAKER () ^ (1ull << 63)) % (r - l + 1) + l;}
#define int long long
const double pi = acos (-1);
//__gnu_pbds :: tree < Key, Mapped, Cmp_Fn = std :: less < Key >, Tag = rb_tree_tag, Node_Upadte = null_tree_node_update, Allocator = std :: allocator < char > > ;
//__gnu_pbds :: tree < PPS, __gnu_pbds :: null_type, less < PPS >, __gnu_pbds :: rb_tree_tag, __gnu_pbds :: tree_order_statistics_node_update > tr;
inline int read () {
	int x = 0, f = 0;
	char c = getchar ();
	for ( ; c < '0' || c > '9' ; c = getchar ()) f |= (c == '-');
	for ( ; c >= '0' && c <= '9' ; c = getchar ()) x = (x << 1) + (x << 3) + (c & 15);
	return !f ? x : -x;
}
const int N = 505, M = 1e4 + 5;
int k, n, e, dp[N][M], mp[N], c[N];
struct store {
	int X, F, C;
	inline bool operator < (store p1) {
		return X < p1.X;
	}
} st[N];
struct seg {
	int a[M], val[M << 2], inc[M << 2];
	inline void push_up (int u) {val[u] = min (val[u << 1], val[u << 1 | 1]);}
	inline void push_down (int u, int l, int r) {
		int mid = l + r >> 1;
		val[u << 1] += inc[u];
		val[u << 1 | 1] += inc[u];
		inc[u << 1] += inc[u], inc[u << 1 | 1] += inc[u];
		inc[u] = 0;
	}
	inline void build (int u, int l, int r) {
		val[u] = inc[u] = 0;
		if (l == r) {
			val[u] = a[l];
			return ;
		}
		int mid = l + r >> 1;
		build (u << 1, l, mid), build (u << 1 | 1, mid + 1, r);
		push_up (u);
	}
	inline void update (int u, int l, int r, int x, int y, int v) {
		if (x <= l && r <= y) {
			val[u] += v;
			inc[u] += v;
			return ;
		}
		push_down (u, l, r);
		int mid = l + r >> 1;
		if (x <= mid) update (u << 1, l, mid, x, y, v);
		if (y > mid) update (u << 1 | 1, mid + 1, r, x, y, v);
		push_up (u);
	}
	inline int query (int u, int l, int r, int x, int y) {
		if (x <= l && r <= y) return val[u];
		push_down (u, l, r);
		int mid = l + r >> 1, ans = 9e18;
		if (x <= mid) ans = min (ans, query (u << 1, l, mid, x, y));
		if (y > mid) ans = min (ans, query (u << 1 | 1, mid + 1, r, x, y));
		return ans;
	}
} tre;
signed main () {
	k = read (), e = read () + 1, n = read ();
	for (int i = 0;i <= n; ++ i) {
		for (int j = 0;j <= k; ++ j) {
			dp[i][j] = 1e18;
		}
	}
	dp[0][0] = 0;
	for (int i = 1;i <= n; ++ i) st[i].X = read () + 1, st[i].F = read (), st[i].C = read ();
	sort (st + 1, st + 1 + n);
	for (int i = 1;i <= n; ++ i) {
		int D = st[i].X - st[i - 1].X;
		for (int j = 0;j <= k; ++ j) dp[i - 1][j] -= st[i].C * j - D * j * j, tre.a[j + 1] = dp[i - 1][j];
		tre.build (1, 1, k + 1);
		for (int j = 0;j <= k; ++ j) {
			int L = j - min (st[i].F, j), R = j;
			dp[i][j] = min (dp[i][j], tre.query (1, 1, k + 1, L + 1, R + 1));
			tre.update (1, 1, k + 1, 1, k + 1, st[i].C);
		}
	}
	printf ("%lld\n", dp[n][k] + (e - st[n].X) * k * k);
	return 0;
}
// Always keep it simple and stupid
posted @ 2023-05-12 12:59  CountingGroup  阅读(38)  评论(0编辑  收藏  举报