VP Educational Codeforces Round 39 (Rated for Div. 2)
A. Partition
题意:把数组分成两部分,加上第一部分,减去第二部分。
显然把正数给第一部分,负数给第二部分。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
int ans = 0;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
ans += std::abs(a[i]);
}
std::cout << ans << "\n";
}
B. Weird Subtraction Process
操作次数很少,模拟就行。
点击查看代码
void solve() {
i64 a, b;
std::cin >> a >> b;
while ((a >= 2 * b || b >= 2 * a) && a != 0 && b != 0) {
a %= 2 * b;
if (a != 0) {
b %= 2 * a;
}
}
std::cout << a << " " << b << "\n";
}
C. String Transformation
题意:给你一个字符串\(s\),你可以把\(s\)中任意字符加到不超过\(z\)。使得字母表作为子序列出现。
从前往后模拟,如果小于当前要匹配的字母,就一直加。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
char cur = 'a';
for (auto & c : s) {
if (cur > 'z') {
break;
}
while (c < cur) {
++ c;
}
if (c == cur) {
++ cur;
}
}
if (cur > 'z') {
std::cout << s << "\n";
} else {
std::cout << -1 << "\n";
}
}
D. Timetable
题意:给你\(n\)个长度\(m\)的01串。每个串的价值为最左边的1和最右边的1的距离。你有\(k\)次操作机会,每次可以把任意一个字符串任意一个位置的1变成0。求最小总价值。
预处理每个字符串操作\(i\)次的最小价值,那么就变成了每个字符串有\(k\)个物品,分别代表做几次操作,操作数就是重量,求总价值最小,做分组背包处理。关于预处理最小价值,可以从前往后存1的位置,然后枚举到一个1就遍历前面1的下标更新保留一些1的最小距离。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
std::vector<std::string> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::vector sum(n, std::vector<int>(k + 1, m));
for (int i = 0; i < n; ++ i) {
std::vector<int> p, val(m + 1, m);
val[0] = 0;
for (int j = 0; j < m; ++ j) {
if (a[i][j] == '1') {
p.push_back(j);
for (int k = (int)p.size() - 1; k >= 0; -- k) {
val[(int)p.size() - k] = std::min(val[(int)p.size() - k], j - p[k] + 1);
}
}
}
int cnt = std::count(a[i].begin(), a[i].end(), '1');
for (int j = 0; j <= k; ++ j) {
sum[i][j] = std::min(sum[i][j], val[std::max(0, cnt - j)]);
}
}
std::vector<int> f(k + 1);
for (int i = 0; i < n; ++ i) {
std::vector<int> g(k + 1, m * n);
for (int j = k; j >= 0; -- j) {
for (int x = 0; x <= j; ++ x) {
g[j] = std::min(g[j], f[j - x] + sum[i][x]);
}
}
f = g;
}
std::cout << f[k] << "\n";
}
E. Largest Beautiful Number
题意:求严格小于\(s\)的最大美丽数。一个数是美丽数那么将它重排可以得到一个没有前导零且是回文的数字。
dfs加剪枝水过了。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
auto get = [&](std::string s) -> std::string {
int n = s.size();
std::reverse(s.begin(), s.end());
for (int i = 0; i < n; ++ i) {
if (s[i] == '0') {
s[i] = '9';
} else {
-- s[i];
break;
}
}
while (s.back() == '0') {
s.pop_back();
}
n = s.size();
if (n & 1) {
-- n;
s.pop_back();
}
std::reverse(s.begin(), s.end());
return s;
};
s = get(s);
if (std::count(s.begin(), s.end(), '1') == 1) {
s = get(s);
}
int n = s.size();
std::string ans, t;
auto dfs = [&](auto & self, int u, bool limit, int st) -> void {
if (ans.size()) {
return;
}
if (__builtin_popcount(st) > n - u) {
return;
}
if (u == n) {
if (st == 0) {
ans = t;
}
return;
}
int up = limit ? s[u] - '0' : 9;
for (int i = up; i >= 0; -- i) {
if (i == 0 && u == 0) {
continue;
}
t += (char)(i + '0');
self(self, u + 1, limit && i == up, st ^ (1 << i));
t.pop_back();
}
};
dfs(dfs, 0, true, 0);
std::cout << ans << "\n";
}
F. Fibonacci String Subsequences
题意:定义\(Fibonacci\)字符串\(f(i)\):
$i = 0, f(i) = $"\(0\)"
$i = 1, f(i) = $"\(1\)"
\(i > 1, f(i) = f(i - 1) + f(i - 2)\)
求\(s\)在\(f(m)\)里作为子序列的出现次数。
考虑\(dp\)。定义\(dp[i][j][k]\)为\(s_{j... k}\)在\(f(i)\)里出现的次数。那么有\(dp[i][j][k] = \sum_{l=j}^{k-1} dp[i-1][j][l] \times dp[i-1][l + 1][k]\)。
同时如果\(k == n\),则\(dp[i][j][k] += dp[i-1][j][k]\times 2^(len_{i-2})\),否则\(dp[i][j][k] += dp[i-1][j][k]\)。
如果\(j == 1\),则\(dp[i][j][k] += dp[i-2][j][k] \times 2^(len_{i-1})\),否则\(dp[i][j][k] += dp[i-2][j][k]\)。
代码省略取模类。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::string s;
std::cin >> s;
std::vector<Z> len(m + 1);
len[0] = len[1] = 2;
for (int i = 2; i <= m; ++ i) {
len[i] = len[i - 1] * len[i - 2];
}
std::vector f(m + 2, std::vector(n, std::vector<Z>(n)));
for (int i = 0; i < n; ++ i) {
f[0][i][i] = (s[i] == '0');
f[1][i][i] = (s[i] == '1');
}
for (int i = 2; i <= m; ++ i) {
for (int j = 0; j < n; ++ j) {
for (int k = j; k < n; ++ k) {
if (k == n - 1) {
f[i][j][k] += f[i - 1][j][k] * len[i - 2];
} else {
f[i][j][k] += f[i - 1][j][k];
}
if (j == 0) {
f[i][j][k] += f[i - 2][j][k] * len[i - 1];
} else {
f[i][j][k] += f[i - 2][j][k];
}
for (int l = j; l < k; ++ l) {
f[i][j][k] += f[i - 1][j][l] * f[i - 2][l + 1][k];
}
}
}
}
std::cout << f[m][0][n - 1] << "\n";
}
G. Almost Increasing Array
题意:给你一个数组\(a\),你可以更改任意位置上的数,使得\(a\)可以删去一个数使得它严格递增。求修改的数最少。
首先不考虑删除,那么问题可以转换为求\(a_i - i\)的最长不下降子序列。因为如果\([j, i]\)都是递增的,那么有\(a_i - a_j >= i - j => a_i - i >= a_j - j\)。
那么答案就是\(n\)减最长不下降子序列的长度。
那么现在考虑删去一个数,那么可以考虑\(f[i][0/1]\)表示到第\(i\)位时前面有没有删去一个数的最长不下降子序列长度。
那么有:
\(f[i][0] = \max_{j=0}^{i-1} f[j][0] + 1, (a_i - i >= a_j - j)\)。
\(f[i][1] = \max_{j=0}^{i-1} f[j][1] + 1, (a_i - i >= a_j - j)\)。注意第一个数不能进行这个转移。
然后考虑\(f[i][1]\)和\(f[j][0]\)的转移,因为我们是选了\(i, j\)的,所以\(j + 1 < i\)。那么有\(f[i][1] = \max_{j=0}^{i-2} f[j][0] + 1, (a_i - i + 1 >= a_j - j)\)。因为中间删了一个数,所以\(a_i\)的下标减一。
那么我们可以用树状数组维护,用一个树状数组维护\([1, i - 1]\)的\(f[i][0]\)的最大值,用一个树状数组维护\([1, i - 1]\)的\(f[i][1]\)的最大值,然后再用一个树状数组维护一个\([1, i - 2]\)的\(f[i][0]\)的最大值。
点击查看代码
template <class T>
struct Fenwick {
int n;
std::vector<T> tr;
Fenwick(int _n) {
init(_n);
}
void init(int _n) {
n = _n;
tr.assign(_n + 1, T{});
}
void add(int x, const T &v) {
for (int i = x; i <= n; i += i & -i) {
tr[i] = tr[i] + v;
}
}
T query(int x) {
T res{};
for (int i = x; i; i -= i & -i) {
res = res + tr[i];
}
return res;
}
T sum(int l, int r) {
return query(r) - query(l - 1);
}
};
struct Info {
int max;
};
Info operator + (const Info & a, const Info & b) {
Info res{};
res.max = std::max(a.max, b.max);
return res;
}
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n), b;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
a[i] -= i;
b.push_back(a[i]);
b.push_back(a[i] + 1);
}
std::sort(b.begin(), b.end());
b.erase(std::unique(b.begin(), b.end()), b.end());
auto get = [&](int x) -> int {
return std::lower_bound(b.begin(), b.end(), x) - b.begin() + 1;
};
int m = b.size();
for (int i = 0; i < n; ++ i) {
a[i] = get(a[i]);
}
Fenwick<Info> tr1(m + 1), tr2(m + 1), tr3(m + 1);
std::vector f(n, std::array<int, 2>{0, 0});
int ans = 0;
for (int i = 0; i < n; ++ i) {
auto [v1] = tr1.query(a[i]);
auto [v2] = tr2.query(a[i]);
auto [v3] = tr3.query(a[i] + 1);
f[i][0] = v1 + 1;
if (i) {
f[i][1] = v2 + 1;
}
if (i > 1) {
f[i][1] = std::max(f[i][1], v3 + 1);
}
tr1.add(a[i], Info{f[i][0]});
tr2.add(a[i], Info{f[i][1]});
if (i) {
tr3.add(a[i - 1], Info{f[i - 1][0]});
}
ans = std::max({ans, f[i][0], f[i][1]});
}
std::cout << std::max(0, n - 1 - ans) << "\n";
}