VP Educational Codeforces Round 59 (Rated for Div. 2)
A. Digits Sequence Dividing
题意:给你一个数字串,把它分成不少于两个子数组。使得这些子数组严格递增。
如果\(n > 2\),可以分成两部分,第一部分是第一个位置,后面的都给第二个部分。
否则如果\(n = 2\),直接判断两个位置的大小。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
if (n > 2) {
std::cout << "YES\n";
std::cout << 2 << "\n";
std::cout << s[0] << " " << s.substr(1) << "\n";
} else {
if (s[0] >= s[1]) {
std::cout << "NO\n";
} else {
std::cout << "YES\n";
std::cout << 2 << "\n";
std::cout << s[0] << " " << s[1] << "\n";
}
}
}
B. Digital root
题意:一个数\(n\)的数位根为一直把这个数变成它的数位和,直到小于等于\(9\)。求数位根为\(x\)的第\(k\)个数。
打表发现,一个数的数位根就是他对\(9\)的模数加一。
你们答案就是\((k - 1) \times 9 + x\)。
点击查看代码
void solve() {
i64 k, x;
std::cin >> k >> x;
std::cout << (k - 1) * 9 + x << "\n";
}
C. Brutality
题意:一个字符串,每个位置有价值为\(a_i\)。你需要选出一个子序列,其价值为所有位置对应的\(a_i\)的和,但要满足序列中没有超过\(k\)个连续相同的子串。求最大价值。
贪心的选,每次考虑一段连续的字符,如果这一段的长度小于等于\(k\),我们可以直接全选,因为后面的字符一定不和当前的相等。
如果大于\(k\),则选最大的\(k\)个数,可以用小根堆维护。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::string s;
std::cin >> s;
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
std::priority_queue<int, std::vector<int>, std::greater<int>> heap;
i64 sum = 0;
int j = i;
while (j < n && s[i] == s[j]) {
if (heap.size() == k) {
if (heap.top() < a[j]) {
sum = sum - heap.top() + a[j];
heap.pop();
heap.push(a[j]);
}
} else {
sum += a[j];
heap.push(a[j]);
}
++ j;
}
ans += sum;
i = j - 1;
}
std::cout << ans << "\n";
}
D. Compression
题意:一个\(n\times n\)的\(01\)矩阵\(a\),要求压缩\(x\)倍得到\(b\),使得\(a[\lceil \frac{i}{x} \rceil][\lceil \frac{j}{x} \rceil] = b[i][j]\)。求最大的\(x\)。
手玩一下发现,如果\(x\)可行,你们每一行可以分成\(\frac{n}{x}\)份,每份长度为\(x\)且都相等。同理列也是。所以我们考虑每一行的连续相同段,那么\(x\)一定是每一段长度的因子,要求最大,那么就是最大公约数。行和列都求一个最大公约数,答案就是它们的最大公约数。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
auto get = [&](const char & c) -> std::string {
int n = c - '0';
if (!isdigit(c)) {
n = 10 + c - 'A';
}
std::string res;
for (int i = 3; i >= 0; -- i) {
if (n >> i & 1) {
res += '1';
} else {
res += '0';
}
}
return res;
};
const int N = 5300;
std::vector<std::bitset<N>> a(n);
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n / 4; ++ j) {
auto b = get(s[i][j]);
for (int k = 0; k < 4; ++ k) {
a[i][j * 4 + k] = b[k] == '1';
}
}
}
int d1 = 0, d2 = 0;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
int k = j;
while (k < n && a[i][j] == a[i][k]) {
++ k;
}
d1 = std::gcd(d1, k - j);
j = k - 1;
}
}
for (int j = 0; j < n; ++ j) {
for (int i = 0; i < n; ++ i) {
int k = i;
while (k < n && a[i][j] == a[k][j]) {
++ k;
}
d2 = std::gcd(d2, k - i);
i = k - 1;
}
}
std::cout << std::gcd(d1, d2) << "\n";
}
E. Vasya and Binary String
题意:一个\(01\)串,如果一段长度为\(k\)的子串每个字符都相等,可以删除这个子串,价值为\(a_k\)。求删除整个字符串的最大代价。
显然是区间\(dp\)。
记\(f[i][j][k]\)为删除\([i, j]\)这个子串,且\(i\)左边包括\(i\)有\(k\)个连续相同字符的最大价值。之所以第三维这样定义,因为删去一些子串后我们无法维护剩下字符拼接得到的字符串的信息,所以我们可以假设\(i\)左边的子串操作后得到\(k\)个连续相同的字符。
那么转移方程如下:
一个是直接删除,把左边连续\(k\)个删掉,剩下是\(f[i + 1][j][0]\),\(f[i][j][k] = a[k] + f[i + 1][j][0]\)。
也可以枚举\(x\),使得\(s[i] = s[x]\),那么得到\(f[i][j][k] = f[i + 1][x - 1][0] + f[x][j][k + 1]\)。,因为删去\([i + 1, x - 1]\)后前面的\(k\)个和\(x\)连接到一起了,所以是\(f[x][j][k + 1]\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
std::vector<i64> a(n + 1);
for (int i = 1; i <= n; ++ i) {
std::cin >> a[i];
}
for (int i = 2; i <= n; ++ i) {
for (int j = 1; j < i; ++ j) {
a[i] = std::max(a[i], a[j] + a[i - j]);
}
}
std::vector f(n + 2, std::vector(n + 2, std::vector<i64>(n + 2)));
for (int len = 1; len <= n; ++ len) {
for (int i = 1; i + len - 1 <= n; ++ i) {
int j = i + len - 1;
for (int k = 1; k <= i; ++ k) {
f[i][j][k] = f[i + 1][j][1] + a[k];
for (int x = i + 1; x <= j; ++ x) {
if (s[i - 1] == s[x - 1]) {
f[i][j][k] = std::max(f[i][j][k], f[i + 1][x - 1][1] + f[x][j][k + 1]);
}
}
}
}
}
std::cout << f[1][n][1] << "\n";
}

浙公网安备 33010602011771号