VP Educational Codeforces Round 74 (Rated for Div. 2)
A. Prime Subtraction
题意:\(x\)一直减质数,能不能到\(y\)。
\(x - y > 1\)就一定可以,因为如果\(x - y\)是偶数,则一直减2,否则减3,然后一直减2。
点击查看代码
void solve() {
i64 x, y;
std::cin >> x >> y;
if (x - y != 1) {
std::cout << "YES\n";
} else {
std::cout << "NO\n";
}
}
B. Kill 'Em All
题意:坐标轴上有\(n\)个怪物,你可以发射炮弹。如果在\(y\)处发射炮弹,那么在\(y\)点的怪物都会死亡,大于\(y\)的怪物会向右移动\(r\),小于\(y\)的会向左移动\(r\)。怪物坐标小于等于\(0\)也会死亡,求最少炮弹数。
把怪物往右边推肯定不优。那么从大到小打,模拟就行。
点击查看代码
void solve() {
int n, r;
std::cin >> n >> r;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::ranges::sort(a);
a.erase(std::unique(a.begin(), a.end()), a.end());
std::ranges::reverse(a);
int ans = 0;
for (int i = 0; i < a.size(); ++ i) {
if (a[i] - (i64)ans * r > 0) {
++ ans;
}
}
std::cout << ans << "\n";
}
C. Standard Free2play
题意:从\(h\)跳到\(0\),有\(n\)个台阶可以使用。如果当前在\(x\),那么可以让\(x, x+1\)这两个台阶切换状态,也就是可以使用的变成不可使用,不可使用变成可以使用。然后你会一直掉落到下一个台阶的位置,但你掉落的高度不能超过零。你可以进行操作,每次强制改变一个台阶的状态,求最少操作次数。
如果当前在\(x\),对于下一个台阶\(y\),可以一直走到\(y+1\)这个位置,此时再走就会使得\(y\)消失,那么\(y\)和它后面的台阶距离不能超过\(1\),否则就需要操作一次。
点击查看代码
void solve() {
int n, h;
std::cin >> h >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
a.push_back(0);
int ans = 0;
for (int i = 1; i < n; ++ i) {
if (a[i] > a[i + 1] + 1) {
++ ans;
} else {
++ i;
}
}
std::cout << ans << "\n";
}
D. AB-string
一个只包含\(AB\)的字符串,如果一个字符串满足每个字符都在一个长度大于\(1\)的子回文串里,这个字符串就是好的。求这个字符串好的子串的个数。
好的不容易求,可以求坏的,那么答案就是总数减坏的子串。
考虑什么子串是坏的,首先这个子串有一个单独的字符,它没有相邻的和它相等的字符。那么就是一段\(AB\)交错的子串,可以发现这个子串长度不能超过\(2\),因为\(ABA, ABAB..\)这些都是回文。那么只可能是\(ABBB...B, BAAA..A, AAAA...AB, BBB..BA\)这些。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::string s;
std::cin >> s;
i64 ans = (i64)n * (n - 1) / 2;
for (int i = 0; i < n; ++ i) {
int j = i;
while (j + 1 < n && s[j + 1] == s[i]) {
++ j;
}
ans -= (i > 0) * (j - i);
ans -= (j + 1 < n) * (j - i + 1);
i = j;
}
std::cout << ans << "\n";
}
E. Keyboard Purchase
题意:一个只包含前\(m\)个小写字母的字符串,你需要选择一个前\(m\)个小写字母组成的排列,然后记的\(i\)个的位置为\(p_i\),那么代价就是\(\sum_{i=1}^{n-1} |p_{s_i} - p_{s_{i+1}}|\)。求最小代价。
\(m\)很小,考虑状压。首先记\(cnt[i][j]\)表示\(i,j\)相邻的对数,\(sun[i]\)表示\(i\)和其它所有字符相邻对的总和。对于一个状态\(s\),如果第\(i\)个字母出现了这一位就是\(1\),那么可以枚举接下来放哪个字符在后面,记加上这个字符有\(x\)个字符,记它和已有的字符的\(cnt\)的和为\(sum1\),和没出现的字符的\(cnt\)和为\(sum2\),那么和前面的代价是\(sum1 \times (x - y)\),和后面的代价是\(sum2 \times (y - x)\)。发现我们不知道\(y\)的值,那么我们可以提前算出来,也就是贡献为\(sum1 \times x - sum2 \times x\),这样不需要知道前面的位置和后面的位置,因为它们拆开并不影响计算。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::string s;
std::cin >> s;
std::array<std::array<int, 26>, 26> cnt{};
for (int i = 0; i + 1 < n; ++ i) {
cnt[s[i] - 'a'][s[i + 1] - 'a'] += 1;
cnt[s[i + 1] - 'a'][s[i] - 'a'] += 1;
}
std::vector<int> sum(m);
for (int i = 0; i < m; ++ i) {
cnt[i][i] = 0;
for (int j = 0; j < m; ++ j) {
sum[i] += cnt[i][j];
}
}
const i64 inf = 1e18;
std::vector<i64> f(1 << m, inf);
f[0] = 0;
for (int i = 1; i < 1 << m; ++ i) {
int p = __builtin_popcount(i);
for (int j = 0; j < m; ++ j) {
if (i >> j & 1) {
i64 sum1 = 0, sum2 = 0;
for (int k = 0; k < m; ++ k) {
if (i >> k & 1) {
sum1 += cnt[j][k];
}
}
sum2 = sum[j] - sum1;
f[i] = std::min(f[i], f[i - (1 << j)] + sum1 * p - sum2 * p);
}
}
}
std::cout << f[(1 << m) - 1] << "\n";
}

浙公网安备 33010602011771号