比赛链接:

https://codeforces.com/contest/1674

E. Breaking the Wall

题目大意:

\(n\) 堵墙,每堵墙有一个生命值,每次可以选择一个位置 \(i\) 进行射击,会使第 \(i\) 堵城墙受到 2 点伤害,第 \(i + 1\) 和第 \(i - 1\) 堵城墙受到 1 点伤害。问最少多少次射击可以使两堵城墙血量 <= 0。

思路:

因为每次射击造成的伤害只在 \(i - 1\)\(i\)\(i + 1\) 上,按照贪心的策略,分三种情况。
1.直接射击最少血量的两堵城墙。
2.破坏相邻的两堵城墙。
这种情况下,如果一堵城墙的血量 <= 另一堵城墙的一半,那么直接射击血量大的那一堵即可。
若没有,可以设射击第一堵城墙 \(k_1\) 次,第二堵 \(k_2\) 次,第一堵城墙的血量为 \(a\),第二堵城墙的血量为 \(b\)
得到关系式:
\(k_1 * 2 + k_2 >= a\)
\(k_1 + 2 * k_2 >= b\)
得到射击次数为 \(k_1 + k_2 = \frac{a + b + 2}{3}\)
3.破坏第 \(i - 1\)\(i + 1\) 这两堵城墙。
首先先射击中间的城墙,当两边的一堵城墙被破坏后,射击还在的另一堵城墙。

代码:

#include <bits/stdc++.h>
using namespace std;
int n, ans = 1e9;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin >> n;
	vector <int> a(n);
	for (int i = 0; i < n; i ++ )
		cin >> a[i];
	for (int i = 1; i < n - 1; i ++ )
		if (a[i - 1] <= a[i + 1]) ans = min(ans, a[i - 1] + (a[i + 1] - a[i - 1] + 1) / 2);
		else ans = min(ans, a[i + 1] + (a[i - 1] - a[i + 1] + 1) / 2);
	for (int i = 0; i < n - 1; i ++ ){
		int x = max(a[i], a[i + 1]), y = min(a[i], a[i + 1]);
		if (y * 2 <= x) ans = min(ans, (x + 1) / 2);
		else ans = min(ans, (x + y + 2) / 3);
	}
	sort(a.begin(), a.end());
	ans = min(ans, (a[0] + 1) / 2 + (a[1] + 1) / 2);
	cout << ans << "\n";
	return 0;
}

F. Desktop Rearrangement

题目大意:

\(n * m\) 的桌面,'*' 为图标,'.' 为空位,当所有图标都填到左上角时(从左至右,从上至下填充),这就是一个干净的桌面。每一操作可以选择其中一个图标放到任意一个空的位置,有 \(q\) 次询问,每次询问让 \((x, y)\) 位置上的图标变成空的,空的变成图标。每次输出让桌面变干净的最小操作次数。

思路:

将图标变成空的时:
如果该位置在要填充的位置上,那么操作次数要增加一次。
有一个特殊情况,因为这步操作之后,要填充的位置数量减少了,如果改变的位置在原来要填充的最后一个位置上,操作次数是不变的。
将空的变成图标时:
如果该位置在要填充的位置上,那么操作次数可以减少一次。
同上述情况可得,如果改变的位置在特殊位置上,那么操作次数是不变的。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int n, m, q, x, y, cnt, ans;
char a[N][N];
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n >> m >> q;
	for (int i = 0; i < n; i ++ )
		for (int j = 0; j < m; j ++ ){
			cin >> a[i][j];
			if (a[i][j] == '*') cnt ++ ;
		}
	for (int i = 0; i < n; i ++ )
		for (int j = 0; j < m; j ++ )
			if (a[i][j] == '*' && j * n + i >= cnt)
				ans ++;
	for (int i = 1; i <= q; i ++ ){
		cin >> x >> y;
		x -- , y -- ;
		int p = y * n + x;
		if (a[x][y] == '*'){
			if (p < cnt) ans ++ ;
			a[x][y] = '.';
			cnt -- ;
			if (a[cnt % n][cnt / n] == '.') ans -- ;
		}
		else{
			if (a[cnt % n][cnt / n] == '.') ans ++ ;
			a[x][y] = '*';
			cnt ++ ;
			if (p < cnt) ans -- ;
		}
		cout << ans << "\n";
	}
	return 0;
}

G. Remove Directed Edges

题目大意:

没有重边、自环的有向无环图,点 \(i\) 的入度为 \(in_i\),出度为 \(out_i\)
可以删除某些边,当且仅当删除后,每个点的出度入度都小于原来该点的出度和入度,或者原来和现在的出度和入度都等于 0。
当某个子图删除了边之后还是连通的,它就被定义成 \(cute\) 的。
\(cute\) 的子图的点数最大可能是多少。

思路:

因为没有环,最后形成的子图会是一条链,因为多余的边都可以删除了。
定义 \(dp[i]\) 为从点 \(i\) 开始的最大的链,进行 \(dfs\)
如果出度为 0 或 1,显然这条边删不掉,删掉的话,两边就分开了,结束搜索。
如果入度为 0 或 1,和出度的情况对应,这个点开始的链和上个点就不能连接起来。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, in[N], out[N], dp[N], ans;
vector <int> g[N];
void dfs(int u){
	if (dp[u]) return;
	dp[u] = 1;
	if (out[u] <= 1) return;
	for (auto v : g[u]){
		dfs(v);
		if (in[v] > 1)
			dp[u] = max(dp[u], dp[v] + 1);
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= m; i ++ ){
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		in[v] ++ ;
		out[u] ++ ;
	}
	for (int i = 1; i <= n; i ++ )
		dfs(i);
	for (int i = 1; i <= n; i ++ )
		ans = max(ans, dp[i]);
	cout << ans << "\n";
	return 0;
}
posted on 2022-05-05 10:37  Hamine  阅读(49)  评论(5编辑  收藏  举报