SGU485 Arrays 题解
卡常题。而且似乎数据比较水。
简而言之,我们需要最大化
先固定
显然
此时
此外,
我们希望给前面
此时可以考虑状压 DP。把左侧没有配对的
直接暴力计算所有可能,每层最多
- 如果是
,那么这是一个未配对的 。 - 如果是
,说明这个和前面的某个位置配对上了。请注意,由于我们是从高往低配对,因此每个 配对的位置一定是最高的 更前面的数。
我们发现,此时无论是
但是此时每层的状态数仍然高达
不过我们可以敏锐的发现,并非每个状态都是有用的。实际上可能并不能达到
我们发现,匹配的过程类似括号串,满足前缀
1 | |
会发现答案大概是
实际上这个
我们注意到你的状态个数之和
此时需要精细实现,基本上是带个 std::unordered_map 在这种数据范围下会很慢,实测就算只计算状态个数就需要
实际上你的值域只有
但是,本题的空间限制为
我们注意到,每一层的
参考代码: 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
82
83
84
85
86
87#include <algorithm>
#include <cassert>
#include <functional>
#include <iostream>
#include <vector>
using uint = unsigned int;
using ll = long long;
using ull = unsigned long long;
namespace solve {
const uint npos = -1;
void solve(uint n) {
std::vector<unsigned short> v(n * 3);
for (size_t i = 0; i < n * 3; i++) {
std::cin >> v[i];
}
std::sort(v.begin(), v.end(), std::greater<unsigned short>());
std::vector<unsigned short> cost(v.begin() + 2 * n, v.end());
std::reverse(cost.begin(), cost.end());
v.resize(2 * n);
std::vector<std::pair<uint, uint>> dp;
dp.emplace_back(0, 0);
std::vector<uint> real_pos(1 << (n - 1), npos);
for (uint i = 0; i < 2 * n; i++) {
std::vector<std::pair<uint, uint>> nxt;
nxt.reserve(8705686);
auto update = [&](uint pos, uint val) {
if (real_pos[pos / 2] == npos) {
real_pos[pos / 2] = nxt.size();
nxt.emplace_back(pos, val);
}
else {
nxt[real_pos[pos / 2]].second =
std::max(nxt[real_pos[pos / 2]].second, val);
}
};
for (const auto &stat : dp) {
const uint cnt = __builtin_popcount(stat.first);
if (cnt != 0) {
const uint prev_pair = (i - cnt) / 2;
const uint highbit = 31 - __builtin_clz(stat.first);
update((stat.first ^ (1 << highbit)) << 1,
v[i] * v[i - 1 - highbit] - v[i] * cost[prev_pair] +
stat.second);
}
if (2 * n - i - 1 < cnt + 1) {
continue;
}
if (!((stat.first >> (n - 1)) & 1)) {
update((stat.first << 1) | 1, stat.second);
}
}
dp.swap(nxt);
for (auto &&i : dp) {
real_pos[i.first / 2] = npos;
}
}
std::cout << dp[0].second << "\n";
return;
}
} // namespace solve
int main() {
uint t, n;
std::cin >> t >> n;
while (t--) {
solve::solve(n);
}
std::cout << std::flush;
return 0;
}