VP Codeforces Round 910 (Div. 2)
A. Milica and String
题意:给你一个只包含\(A\)和\(B\)的串,你每次可以让一个前缀都变成\(A\)或者都变成\(B\),问让字符串恰好有\(k\)个\(B\)的最小操作数和方案。
我们最多操作一次。如果我们\(B\)的数量大于\(k\),那么我们找一个恰好有\(k-cnt_B\)个\(B\)的前缀全部变成\(A\)就行了。小于\(k\)同理。恰好\(k\)个就不用操作。所以我们最多操作一次。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
int cnt = std::count(s.begin(), s.end(), 'B');
if (cnt > k) {
for (int i = 0; i < n; ++ i) {
if (s[i] == 'B') {
-- cnt;
}
if (cnt == k) {
std::cout << 1 << "\n";
std::cout << i + 1 << " A" << "\n";
return;
}
}
} else if (cnt < k) {
for (int i = 0; i < n; ++ i) {
if (s[i] == 'A') {
++ cnt;
}
if (cnt == k) {
std::cout << 1 << "\n";
std::cout << i + 1 << " B" << "\n";
return;
}
}
} else {
std::cout << 0 << "\n";
}
}
B. Milena and Admirer
题意:你每次可以把\(a_i\)变成\(x, a_i-x\)这个两个数代替它\(x<a_i\)。问让数组递增的最小操作数。
我们从后往前做,那么对于\(a_i\)我们希望它分出来的这些数里最小的最大。可以二分找到这个最大值。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
i64 ans = 0;
for (int i = n - 2; i >= 0; -- i) {
if (a[i] > a[i + 1]) {
if (a[i] % a[i + 1] == 0) {
ans += a[i] / a[i + 1] - 1;
a[i] = a[i + 1];
} else {
i64 cnt = a[i] / a[i + 1];
a[i] %= a[i + 1];
ans += cnt;
i64 l = a[i], r = a[i + 1];
while (l < r) {
i64 mid = l + r + 1 >> 1ll;
if (mid <= a[i + 1] - (mid - a[i] + cnt - 1) / cnt) {
l = mid;
} else {
r = mid - 1;
}
}
a[i] = l;
}
}
}
std::cout << ans << "\n";
}
C. Colorful Grid
题意:一个网格,你要在网格线上涂蓝色或者红色,满足有一条起点为左上角终点为右下角经过了\(k\)条线的路径。每个点和线都可以多次经过。
显然我们至少要走\(n-1+m-1\)条线才能到终点。考虑多出来的怎么走。如果\((k-(n-1+m-1))\%4==0\),那么我们让其到达终点后一直围着一个格子转。如果\((k-(n-1+m-1))\%2==0\),可以让他到\((n,m-1)\)时往上走绕一个格子下来,这样就回到了模\(4\)等于\(0\)的情况。其他情况没有解。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
if (k < n - 1 + m - 1 || (k - n - 1 + m - 1) % 2) {
std::cout << "NO\n";
return;
}
std::cout << "YES\n";
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m - 1; ++ j) {
std::cout << (j % 2 ? 'R' : 'B') << " \n"[j == m - 2];
}
}
std::string s[2] = {"B", "R"};
int t = n % 2 == 0;
for (int i = 0; i + 2 < n; ++ i) {
for (int j = 0; j < m; ++ j) {
std::cout << s[t] << " \n"[j == m - 1];
t ^= 1;
}
if (m % 2 == 0) {
t ^= 1;
}
}
std::cout << s[t] << " ";
if (m % 2) {
for (int j = 1; j < m; ++ j) {
std::cout << "B ";
}
std::cout << "\n";
} else {
for (int j = 1; j < m; ++ j) {
std::cout << "R ";
}
std::cout << "\n";
}
}
D. Absolute Beauty
题意:给你两个数组\(a,b\),价值为\(\sum_{i=1}^{n} |a_i-b_i|\),你可以至多交换一次\(b\)里的两个元素,求最大价值。
转换题意,如果\(a_i>b_i\)则交换\(a_i,b_i\),那么我们有\(n\)条\([a_i,b_i]\)的线段,我们可以给两个线段交换端点使得总线段和最大。模拟不相交、相交和包含三种情况发现,只有不相交的线段才会使得答案增大,增大为两个线段中间长度的两倍,于是找最左的右端点和最右的左端点即可。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<i64> a(n), b(n);
i64 ans = 0;
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
}
i64 max = 0, min = 2e9;
for (int i = 0; i < n; ++ i) {
ans += std::abs(a[i] - b[i]);
max = std::max(max, std::min(a[i], b[i]));
min = std::min(min, std::max(a[i], b[i]));
}
ans += std::max(0ll, max - min) * 2;
std::cout << ans << "\n";
}
E. Sofia and Strings
题意:给你两个字符串\(s, t\),你每次可以删除\(s\)中某个字符,或者给\(s\)一个区间排序。问\(s\)能不能变成\(t\)。
我们可以把排序操作看成让一个字符左移,如果我们想让\(s_i\)移到\(j\)这个位置,那么\([j, i]\)里小于\(s_i\)都要删掉,那么\(s_i\)就可以移动到\(j\)。如果没有这两个操作,只是单纯看\(t\)是不是\(s\)的子序列,我们可以两个指针维护\(t_j\)在\(s\)中匹配的到最早的位置\(i\),我们尽可能让最前面的匹配,因为匹配到的地方的前面的字符都不可能在匹配了,为了有更多机会应该让更前面的能匹配就匹配。到这题也是一样,每次匹配最前面的,然后因为它前面的比它小的不能看排到它后面了,就应该删掉。所以我们用\(26\)个队列存每个字符出现的位置,然后贪心匹配,每次把位置小于当前位置和字符也小于当前字符的删掉就行。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::string s, t;
std::cin >> s >> t;
std::vector pos(26, std::queue<int>{});
for (int i = 0; i < n; ++ i) {
pos[s[i] - 'a'].push(i);
}
for (int i = 0; i < m; ++ i) {
if (pos[t[i] - 'a'].empty()) {
std::cout << "NO\n";
return;
}
int j = pos[t[i] - 'a'].front(); pos[t[i] - 'a'].pop();
for (int k = 0; k < t[i] - 'a'; ++ k) {
while (pos[k].size() && pos[k].front() < j) {
pos[k].pop();
}
}
}
std::cout << "YES\n";
}
F. Vova Escapes the Matrix
题意:给你一个矩阵,有障碍,有空地,有一个起点。不能经过障碍,看起点能到几个边界格子分为三种类型:
- 没有出口。
- 有一个出口。
- 有两个以上出口。
你可以把某些空地变成障碍,但不能改变类型。问最多改几个空地。
如果没有出口,那就都堵上,如果有一个出口,那么就是不能堵到出口的最短路径上的点。
如果有两个以上出口,那么我们要保留两个出口,其他都堵住。要让保留的出口的长度加起来最小(重合部分只算一次),那么我们可以以每个出口为起点跑\(bfs\),求出每个点到出口的最短路和次短路。那么我们可以枚举两段路径分开的点,求得距离为起点到这个点的距离加到出口的最短路加次短路。取最小值就行,因为如果最短路和次短路重合,那么这个距离肯定比不重合的大,所以一定能取到合法的最小值。
注意\(bfs\)中要避免一个出口到一个点又回来的情况,记录每个点的最短路和次短路是哪个出口过来的,发现有一样的就不要跑。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::string> s(n);
for (int i = 0; i < n; ++ i) {
std::cin >> s[i];
}
int sx = -1, sy = -1;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (s[i][j] == 'V') {
sx = i, sy = j;
}
}
}
const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
const int inf = 1e8;
std::vector d(n, std::vector<int>(m, inf));
std::queue<std::pair<int, int> > q;
q.push({sx, sy});
d[sx][sy] = 0;
while (q.size()) {
auto [x, y] = q.front(); q.pop();
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || s[nx][ny] == '#' || d[nx][ny] != inf) {
continue;
}
d[nx][ny] = d[x][y] + 1;
q.push({nx, ny});
}
}
std::vector<std::pair<int, int> > a;
int sum = 0;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if ((i == 0 || i == n - 1 || j == 0 || j == m - 1) && d[i][j] != inf) {
a.push_back({i, j});
}
if (s[i][j] == '.') {
++ sum;
}
}
}
if (a.size() == 0) {
std::cout << sum << "\n";
} else if (a.size() == 1) {
std::cout << sum - d[a[0].first][a[0].second] << "\n";
} else {
std::vector dist(n, std::vector(m, std::array<int, 2>{inf, inf}));
std::vector id(n, std::vector(m, std::array<int, 2>{inf, inf}));
std::queue<std::array<int, 3> > q;
for (auto & [x, y] : a) {
dist[x][y][0] = 0;
q.push({x, y, 0});
id[x][y][0] = x * m + y + 1;
}
while (q.size()) {
auto [x, y, t] = q.front(); q.pop();
for (int i = 0; i < 4; ++ i) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || nx >= n || ny < 0 || ny >= m || s[nx][ny] == '#') {
continue;
}
if (id[x][y][t] == id[nx][ny][0] || id[x][y][t] == id[nx][ny][1]) {
continue;
}
if (dist[x][y][t] + 1 < dist[nx][ny][0]) {
dist[nx][ny][1] = dist[nx][ny][0];
id[nx][ny][1] = id[nx][ny][0];
dist[nx][ny][0] = dist[x][y][t] + 1;
id[nx][ny][0] = id[x][y][t];
q.push({nx, ny, 0});
} else if (dist[x][y][t] + 1 == dist[nx][ny][0]) {
dist[nx][ny][1] = dist[x][y][t] + 1;
id[nx][ny][1] = id[x][y][t];
q.push({nx, ny, 1});
} else if (dist[x][y][t] + 1 < dist[nx][ny][1]) {
dist[nx][ny][1] = dist[x][y][t] + 1;
id[nx][ny][1] = id[x][y][t];
q.push({nx, ny, 1});
}
}
}
int ans = 0;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < m; ++ j) {
if (d[i][j] != inf && dist[i][j][0] != inf && dist[i][j][1] != inf) {
ans = std::max(ans, sum - (d[i][j] + dist[i][j][0] + dist[i][j][1]));
}
}
}
std::cout << ans << "\n";
}
}

浙公网安备 33010602011771号