Neowise Labs Contest 1 (Codeforces Round 1018, Div. 1 + Div. 2)
A. Wonderful Sticks
题意:给你一个字符串,构造一个排列,满足如果\(s_i =\)'>'则\(p_{i + 1}\)大于前面所有数,否则小于前面所有数。
从后往前构造,遇到小于号放当前最小的数,否则放最大的数。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
std::vector<int> a(n);
int l = 0, r = n - 1;
for (int i = n - 2; i >= 0; -- i) {
if (s[i] == '>') {
a[i + 1] = r -- ;
} else {
a[i + 1] = l ++ ;
}
}
a[0] = l;
for (int i = 0; i < n; ++ i) {
std::cout << a[i] + 1 << " \n"[i == n - 1];
}
}
B. Wonderful Gloves
题意:\(n\)种手套,每种手套分左右,分别有\(l_i, r_i\)个。如果一种手套左右分别被拿了一个,就凑齐了着一类手套。问凑起\(k\)对手套需要拿几次。
考虑最坏的情况,从每对手套里都完整的一类拿出来,也就是\(\sum_{i=1}^{n} \max(l_i, r_i)\)。这样我们一对手套还没凑齐,然后剩下的数里选前\(k-1\)大的拿走,然后答案加一就行。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> l(n), r(n);
for (int i = 0; i < n; ++ i) {
std::cin >> l[i];
}
for (int i = 0; i < n; ++ i) {
std::cin >> r[i];
}
std::vector<int> a;
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
if (l[i] < r[i]) {
std::swap(l[i], r[i]);
}
ans += l[i];
a.push_back(r[i]);
}
std::ranges::sort(a, std::greater<>());
for (int i = 0; i + 1 < k; ++ i) {
ans += a[i];
}
std::cout << ans + 1 << "\n";
}
C. Wonderful City
题意:一个矩阵,你可以对每行每列最多操作一次,将这一行这一列全部数加一,代价分别为\(a_i, b_j\)。问没有两个相邻数相等的最小代价。
发现行列可以分开讨论,因为操作行不会影响这一行相邻两个数的差,操作列不会影响这一列相邻两个数的差。那么做两次\(dp\)。以行为例:\(f[i][0/1]\)表示第\(i\)行有没有操作的最小代价。那么枚举下一行有没有操作,然后加上操作值后判断这两行是不是合法的,也就是没有相邻两个数相等。如果合法就可以转移\(f[i + 1][y] = \min(f[i + 1][y] + [y == 1]a_{i+1}, f[i][x])\)。
列同理,最后两个加起来就行。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::vector<i64>> h(n, std::vector<i64>(n));
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
std::cin >> h[i][j];
}
}
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector<i64> b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
}
const i64 inf = 1e18;
std::array<i64, 2> f{inf, inf};
f[0] = 0; f[1] = a[0];
for (int i = 0; i + 1 < n; ++ i) {
std::array<i64, 2> g{inf, inf};
for (int x = 0; x < 2; ++ x) {
for (int y = 0; y < 2; ++ y) {
bool flag = true;
for (int j = 0; j < n; ++ j) {
if (h[i][j] + x == h[i + 1][j] + y) {
flag = false;
break;
}
}
if (flag) {
g[y] = std::min(g[y], f[x] + y * a[i + 1]);
}
}
}
f = g;
}
i64 ans = std::min(f[0], f[1]);
if (ans == inf) {
std::cout << -1 << "\n";
return;
}
f[0] = 0; f[1] = b[0];
for (int j = 0; j + 1 < n; ++ j) {
std::array<i64, 2> g{inf, inf};
for (int x = 0; x < 2; ++ x) {
for (int y = 0; y < 2; ++ y) {
bool flag = true;
for (int i = 0; i < n; ++ i) {
if (h[i][j] + x == h[i][j + 1] + y) {
flag = false;
break;
}
}
if (flag) {
g[y] = std::min(g[y], f[x] + y * b[j + 1]);
}
}
}
f = g;
}
i64 ans1 = std::min(f[0], f[1]);
if (ans1 == inf) {
std::cout << -1 << "\n";
return;
}
std::cout << ans + ans1 << "\n";
}
D. Wonderful Lightbulbs
题意:一开始有一个位置是亮的,随后进行了若干次操作,每次操作选择一个\((x, y)\),然后更改\((x, y), (x + 1, y), (x, y + 1), (x, + 1, y - 1)\)的状态。选择给出操作后亮着的灯,求一开始的位置。
亮着的灯一定是奇数,因为每次改变四个灯,而一开始亮着的灯是\(1\)个,也就是奇数个,每次改变偶数个依然是奇数。
然后假设一开始是\((x, y)\)亮着,那么如果\(x' = x\)的位置一定是奇数个亮着,因为这一列每次操作了\((x, y), (x, y + 1)\)两个。同理对角线\(x' + y' = x + y\)上的灯也是奇数个。那么我们只需要记录\(x\)坐标亮着灯的个数,和每个对角线灯的个数。找两个奇数个的,它们的交点就是答案。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::pair<int, int>> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i].first >> a[i].second;
}
std::map<int, int> X, diag;
for (auto & [x, y] : a) {
++ X[x];
++ diag[x + y];
}
int x = 0, y = 0;
for (auto & [z, cnt] : X) {
if (cnt & 1) {
x = z;
break;
}
}
for (auto & [z, cnt] : diag) {
if (cnt & 1) {
y = z - x;
break;
}
}
std::cout << x << " " << y << "\n";
}
E. Wonderful Teddy Bears
题意:一个只有\(PB\)的字符串,要把\(B\)都移到左边,\(P\)都移到右边。每次给一个长度为\(3\)的子串排序。求最少操作次数。
可以往逆序对的方向想。那么我们可以操作\(PBB, PPB\)这两个,每次使得逆序对减少\(2\)。那么至少需要逆序对数除\(2\)的操作数。这是理想情况,但实际可能有\(PBPBPBP..\)这样交错的子串。
设有\(cnt1\)个\(B\),有\(cnt2\)个在偶数位置上,因为排序后一定是\(\lfloor \frac{cnt1}{2} \rfloor\)个\(B\)在偶数位置上,同时发现操作\(PBB, PPB\)不会改变\(cnt2\)的个数。于是我们只能借助\(PBP, BPB\)来改变\(cnt2\)的个数。那么需要\(\lfloor \frac{cnt1}{2} \rfloor - cnt2\)次操作,这样序列就会变成我们想要的样子,就可以按开始的操作方式来做了。同时发现操作\(PBP, BPB\)会使得逆序对减少\(1\),那么记一开始逆序对为\(cnt\)个。那么剩下\(cnt - (\lfloor \frac{cnt1}{2} \rfloor - cnt2)\)个逆序对,这一部分操作数为\(\frac{cnt - (\lfloor \frac{cnt1}{2} \rfloor - cnt2)}{2}\),另一部分操作数为\(\lfloor \frac{cnt1}{2} \rfloor - cnt2\),加起来就是\(\frac{cnt + (\lfloor \frac{cnt1}{2} \rfloor - cnt2)}{2}\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
i64 ans = 0, cnt1 = 0, cnt2 = 0;
for (int i = n - 1; i >= 0; -- i) {
if (s[i] == 'B') {
++ cnt1;
if (i & 1) {
++ cnt2;
}
} else {
ans += cnt1;
}
}
ans = (ans + std::abs(cnt1 / 2 - cnt2));
std::cout << ans / 2 << "\n";
}