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

题意:给你一个矩阵,有障碍,有空地,有一个起点。不能经过障碍,看起点能到几个边界格子分为三种类型:

  1. 没有出口。
  2. 有一个出口。
  3. 有两个以上出口。

你可以把某些空地变成障碍,但不能改变类型。问最多改几个空地。

如果没有出口,那就都堵上,如果有一个出口,那么就是不能堵到出口的最短路径上的点。
如果有两个以上出口,那么我们要保留两个出口,其他都堵住。要让保留的出口的长度加起来最小(重合部分只算一次),那么我们可以以每个出口为起点跑\(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";
    }
}
posted @ 2025-01-19 14:40  maburb  阅读(29)  评论(0)    收藏  举报