QOJ #7645. Shoes 题解

Description

尼基塔计划度假时,决定好好享受一下并为自己买一双新鞋。为此,他研究了位于他酒店所在街道上的各家商店,并选择了 nn 双鞋来试穿。尼基塔知道,每双鞋需要 kk 秒钟来试穿,并打算每天安排 TT 秒钟的时间来访问商店。街道是一个坐标轴,移动速度为每秒一单位,酒店位于原点,每次访问商店都必须从酒店出发并返回。求尼基塔试穿完所有他感兴趣的鞋子所需要的最少假期天数。

n104n\leq 10^4

Solution

显然一个方案的时间只与最左和最右端点有关,所以我们可以把每次行走抽象成灾区间 [l,r][l,r] 可以选 cc 个买。

然后有个观察是我们选择的区间只会互相包含和不交,否则设 c1c_1c2c_2 分别表示两个区间可以买的个数,那么一定可以调整成前 c1c_1 个和后 c2c_2 个放一起。

考虑区间 dp。

fl,rf_{l,r} 表示只考虑 [l,r][l,r] 的子区间,将 [l,r][l,r] 消掉的最小天数。

首先如果不操作 [l,r][l,r] 这整个区间,那么一定存在一个最优方案使得 [l,p][l,p] 或者 [p,r][p,r] 可以买的个数等于区间长度,这个 O(n)O(n) 预处理出来最远的端点可以做到 O(1)O(1) 转移。

如果操作 [l,r][l,r],设 cc 表示 [l,r][l,r] 的权值。那么剩下的 lenclen-c 个没有被这次删掉的点一定构成一段区间,否则同样能够调整。这部分转移形如:fl,rfi,i+lenc1+1f_{l,r}\leftarrow f_{i,i+len-c-1}+1

按照 rr 从小到大转移的话这就是查询后缀最小值,对于每个长度 kk,维护 nxtk,inxt_{k,i} 表示 ii 右边第一个权值小于 fi,i+k1f_{i,i+k-1} 的位置,这样查询时可以并查集维护。每次加入新区间只需要判断所有没找到 nxtnxt 的能否更新即可。

时间复杂度:O(n2logn)O(n^2\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <bits/stdc++.h>

// #define int int64_t

using i64 = int64_t;

const int kMaxN = 1e4 + 5;

int n; i64 k, t, a[kMaxN];
int f[kMaxN][kMaxN], fa[kMaxN][kMaxN], pre[kMaxN], nxt[kMaxN];
std::vector<int> vec[kMaxN];

inline void chkmax(int &x, int y) { x = (x > y ? x : y); }
inline void chkmin(int &x, int y) { x = (x < y ? x : y); }

int calc(int x, int y) {
i64 s = llabs(a[x]) + llabs(a[y]) + llabs(a[y] - a[x]);
if (s <= t) return std::min<i64>((t - s) / k, n);
else return 0;
}

int find(int *fa, int x) { return x == fa[x] ? x : fa[x] = find(fa, fa[x]); }

void dickdreamer() {
std::cin >> n >> k >> t;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
std::sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; ++i) {
pre[i] = i + 1;
for (int j = i; j; --j) {
if (calc(j, i) >= i - j + 1) pre[i] = j;
else break;
}
nxt[i] = i - 1;
for (int j = i; j <= n; ++j) {
if (calc(i, j) >= j - i + 1) nxt[i] = j;
else break;
}
}
for (int len = 1; len <= n; ++len) {
for (int i = 1; i <= n - len + 1; ++i)
fa[len][i] = i;
}
memset(f, 0x3f, sizeof(f));
for (int r = 1; r <= n; ++r) {
for (int l = r; l; --l) {
int c = calc(l, r);
if (c >= r - l + 1) {
f[l][r] = 1;
} else {
if (l == r) continue;
int len = r - l + 1 - c;
f[l][r] = std::min(f[nxt[l] + 1][r] + 1, f[l][pre[r] - 1] + 1);
int pos = find(fa[len], l);
chkmin(f[l][r], f[pos][pos + len - 1] + 1);
// for (int i = l; i <= r - len + 1; ++i) chkmin(f[l][r], f[i][i + len - 1] + 1);
}
int len = r - l + 1;
for (; vec[len].size(); vec[len].pop_back()) {
int x = vec[len].back();
if (f[x][x + len - 1] < f[l][r]) break;
fa[len][x] = l;
}
vec[len].emplace_back(l);
}
}
std::cout << f[1][n] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

QOJ #7645. Shoes 题解
https://sobaliuziao.github.io/2025/08/15/post/27bd09c.html
作者
Egg_laying_master
发布于
2025年8月15日
许可协议