VP Codeforces Round 892 (Div. 2)
A. United We Stand
题意:把\(a\)分成两个不为空的数组,使得第二个数组的任意一个数不是第一个数组任意一个数的因子。
如果第二个数组的数都比第一个大就符合条件。无解的情况就是数组的数都相同。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n), b, c;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end());
if (a[0] == a.back()) {
std::cout << -1 << "\n";
return;
}
for (int i = 0; i < n; ++ i) {
if (a[i] != a.back()) {
b.push_back(a[i]);
} else {
c.push_back(a[i]);
}
}
std::cout << b.size() << " " << c.size() << "\n";
for (int i = 0; i < b.size(); ++ i) {
std::cout << b[i] << " ";
}
std::cout << "\n";
for (int i = 0; i < c.size(); ++ i) {
std::cout << c[i] << " ";
}
std::cout << "\n";
}
B. Olya and Game with Arrays
题意:\(n\)个数组每个数组把一个元素给其它数组,然后价值为每个数组的最小值之和。
先把每个数组的最小值拿出来,然后给剩下数组里最小值最小的那个数组。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::vector<int>> a(n);
int min = 2e9;
for (int i = 0; i < n; ++ i) {
int m;
std::cin >> m;
for (int j = 0; j < m; ++ j) {
int x;
std::cin >> x;
a[i].push_back(x);
}
std::sort(a[i].begin(), a[i].end(), std::greater<int>());
min = std::min(min, a[i].back());
a[i].pop_back();
}
int min1 = 2e9;
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
ans += a[i].back();
min1 = std::min(min1, a[i].back());
}
ans = ans - min1 + min;
std::cout << ans << "\n";
}
C. Another Permutation Problem
题意:排列的价值为\((\sum_{i=1}^{n} p_i \times i) - (\max_{i=1}^{n} p_i \times i)\),求最大价值。
打表发现最大价值的数组一定使翻转了某个后缀。枚举翻转的后缀就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
i64 ans = 0;
for (int k = 1; k <= n + 1; ++ k) {
i64 sum = 0, max = 0;
i64 x = 1;
for (int i = 1; i < k; ++ i) {
sum += x * i;
max = std::max(max, x * i);
++ x;
}
x = n;
for (int i = k; i <= n; ++ i) {
sum += x * i;
max = std::max(max, x * i);
-- x;
}
ans = std::max(ans, sum - max);
}
std::cout << ans << "\n";
}
D. Andrey and Escape from Capygrad
题意:\(n\)个四元组\((l_i, r_i, x_i, y_i)\)表示可以从\([l_i, r_i]\)任意一个位置到\([x_i, y_i]\)任意一个位置。\(q\)次询问每次问\(x\)最远能走到哪里。
先把出现过的数都离散化。
按照\(r_i\)从大到小排序,然后从大到小枚举\(i\),每次把\(r_i == i\)的加进大根堆,并用\(multiset\)记录\(y_i\)的值。这样就表示了所有覆盖\(i\)线段可以让\(i\)走到的最远距离。用并查集把\(i\)和\(\max y_i\)合并。
点击查看代码
struct DSU {
std::vector<int> fa, cnt;
DSU(int _n) {
init(_n);
}
void init(int _n) {
fa.assign(_n, 0);
cnt.assign(_n, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return false;
}
fa[y] = x;
cnt[x] += cnt[y];
return true;
}
bool same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return cnt[find(x)];
}
};
void solve() {
int n;
std::cin >> n;
std::vector<std::array<int, 4>> a(n);
std::vector<int> b;
for (int i = 0; i < n; ++ i) {
int l, r, L, R;
std::cin >> l >> r >> L >> R;
a[i] = {l, r, L, R};
b.push_back(l);
b.push_back(r);
b.push_back(L);
b.push_back(R);
}
int q;
std::cin >> q;
std::vector<int> Q(q);
for (int i = 0; i < q; ++ i) {
std::cin >> Q[i];
b.push_back(Q[i]);
}
std::sort(b.begin(), b.end());
b.erase(std::unique(b.begin(), b.end()), b.end());
int m = b.size();
auto get = [&](int x) -> int {
return std::lower_bound(b.begin(), b.end(), x) - b.begin();
};
for (auto & [l, r, L, R] : a) {
l = get(l);
r = get(r);
L = get(L);
R = get(R);
}
for (auto & x : Q) {
x = get(x);
}
DSU dsu(m);
std::sort(a.begin(), a.end(), [&](std::array<int, 4> & a, std::array<int, 4> & b) {
return a[1] > b[1];
});
std::priority_queue<std::pair<int, int>> heap;
std::multiset<int> s;
for (int i = m - 1, j = 0; i >= 0; -- i) {
while (heap.size() && heap.top().first > i) {
s.extract(heap.top().second);
heap.pop();
}
while (j < n && a[j][1] == i) {
heap.push({a[j][0], a[j][3]});
s.insert(a[j][3]);
++ j;
}
if (s.size() && *s.rbegin() > i) {
dsu.merge(*s.rbegin(), i);
}
}
for (int i = 0; i < q; ++ i) {
std::cout << b[dsu.find(Q[i])] << " \n"[i == q - 1];
}
}
E. Maximum Monogonosity
题意:给你两个数组\(a, b\),一个线段的价值为\(|b_l - a_r| + |b_r - a_l|\)。你要使用总长度为\(k\)的线段。求最大价值。
记\(f[i][j]\)表示前\(i\)个位置用了总长度为\(j\)的最大值,转移方程就是枚举包含\(i\)的这一段有多长:\(f[i][j] = \max_{k=1}^{j} f[i - k][j - k] + |b_{i - k + 1} - a_i| + |b_i - a_{i-k+1}|\)。但这样是\(O(n^3)\)的时间复杂度,无法通过。先把绝对值拆开,总共有四种:
- \(f[i - k][j - k] + b_{i - k + 1} - a_i + b_i - a_{i - k + 1}\)
- \(f[i - k][j - k] + a_i - b_{i - k + 1} + b_i - a_{i - k + 1}\)
- \(f[i - k][j - k] + b_{i - k + 1} - a_i + a_{i - k + 1} - b_i\)
- \(f[i - k][j - k] + a_i - b_{i - k + 1} + a_{i - k + 1} - b_i\)
同时发现\(i\)和\(j\)都是减\(k\),那么能转移到\(i, j\)的\(x, y\)都满足\(i - j = x - y\)。那么我们可以\(g[l][t], t \in \{0, 1, 2, 3\}\)表示\(i - j = l\)的四种情况的\(f[i][j]\)的最大值。那么就可以拆开就行转移了。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n + 1), b(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
for (int i = 1; i <= n; ++ i) {
std::cin >> b[i];
}
const i64 inf = 1e18;
std::vector f(n + 1, std::vector<i64>(k + 1, -inf));
std::vector g(n + 1, std::array<i64, 4>{-inf, -inf, -inf, -inf});
f[0][0] = 0;
//f[i][j] = f[i - k][j - k] + b[i - k + 1] - a[i] + b[i] - a[i - k + 1];
//f[i][j] = f[i - k][j - k] + a[i] - b[i - k + 1] + b[i] - a[i - k + 1];
//f[i][j] = f[i - k][j - k] + b[i - k + 1] - a[i] + a[i - k + 1] - b[i];
//f[i][j] = f[i - k][j - k] + a[i] - b[i - k + 1] + a[i - k + 1] - b[i]
for (int i = 1; i <= n; ++ i) {
f[i][0] = 0;
for (int j = 1; j <= std::min(k, i); ++ j) {
f[i][j] = f[i - 1][j];
g[i - j][0] = std::max(g[i - j][0], f[i - 1][j - 1] + b[i] - a[i]);
g[i - j][1] = std::max(g[i - j][1], f[i - 1][j - 1] - b[i] - a[i]);
g[i - j][2] = std::max(g[i - j][2], f[i - 1][j - 1] + b[i] + a[i]);
g[i - j][3] = std::max(g[i - j][3], f[i - 1][j - 1] - b[i] + a[i]);
f[i][j] = std::max(f[i][j], g[i - j][0] - a[i] + b[i]);
f[i][j] = std::max(f[i][j], g[i - j][1] + a[i] + b[i]);
f[i][j] = std::max(f[i][j], g[i - j][2] - a[i] - b[i]);
f[i][j] = std::max(f[i][j], g[i - j][3] + a[i] - b[i]);
}
}
std::cout << f[n][k] << "\n";
}