Codeforces1651-Educational Codeforces Round 124 (Rated for Div. 2)-题解(A-D)

比赛链接

A

题意

2 n 2^n 2n名运动员比赛,当运动员 x 和 y 比赛时,获胜者的决定如下:

  • 如果 x + y x+y x+y是奇数,则编号较低的运动员获胜
  • 如果 x + y x+y x+y是偶数,则编号较高的运动员获胜。

思路

最后肯定是最大的奇数获胜,也就是 2 n − 1 2^n - 1 2n1

AC代码

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
	int t;
	cin >> t;
	while(t--) {
		int n;
		cin >> n;
		cout << (1 << n) - 1 << '\n';
	}
 
    return 0;
}

B

题意

有一个数组,可以做一次下面的操作

  • 选择两个不同的下标 i , j i,j i,j
  • a i = a j = ∣ a i − a j ∣ a_i = a_j = |a_i - a_j| ai=aj=aiaj

你需要构造一个数组 a , a i ∈ [ 1 , 1 0 9 ] a,a_i∈[1,10^9] aai[1,109],使得对于任意的 i , j i,j i,j都有执行完上边的操作后,数组的和不会变小。

思路

数组 [ 1 , 3 , 9 , 27.... 3 n ] [1, 3, 9, 27....3^n] [1,3,9,27....3n]即为最优解,我们只需要判断 3 n 3^n 3n是否在 1 0 9 10^9 109之内就可以确定是否有解

AC代码

#include <bits/stdc++.h>
 
using namespace std;
using ll = long long;
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
	ll tmp = 1;
	vector<ll> p;
	for(int i = 1;tmp <= 1e9;tmp *= 3) p.push_back(tmp);
 
	int t;
	cin >> t;
	
	while(t--) {
		int n;
		cin >> n;
		if(n > p.size()) cout << "NO" << '\n';
		else {
			cout << "YES" << '\n';
			for(int i = 0;i < n;i++) cout << p[i] << ' ';cout << '\n';
		}
	}
 
    return 0;
}

C

题意

最开始有两排电脑,他们的权值分别是 a i , b i a_i,b_i ai,bi,对于每排电脑,相邻的两个电脑已经被电线连接,我们可以任选两台电脑然后用电线给他进行连接,需要的代价为 ∣ a i − b i ∣ |a_i - b_i| aibi,求最小的连线代价,使得在破坏任意一条电线后,两排电脑属于一个连通块内。

思路

不难发现,只有两排电脑的最左端和右端的电脑需要被连接,所以最少可以需要两条电线,最多需要四条电线,所以可以暴力枚举

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int t;
	cin >> t;
	while(t--) {
		int n;
		cin >> n;
		vector<int> a(n + 1), b(n + 1);
		for(int i = 1;i <= n;i++) cin >> a[i];
		for(int i = 1;i <= n;i++) cin >> b[i];
		auto dis = [&](int x, int y) -> ll {
			return abs(a[x] - b[y]);
		};
		auto cal = [&](int x, int y) -> ll {
			ll ret = 0x3f3f3f3f3f3fll;
			if(x) {
				for(int i = 1;i <= n;i++) ret = min(ret, dis(x, i));
			}
			else {
				for(int i = 1;i <= n;i++) ret = min(ret, dis(i, y));
			}
			return ret;
		};
		ll ans = 0x3f3f3f3f3f3f3fll;

		ans = min(ans, dis(1, n) + dis(n, 1));
		ans = min(ans, dis(1, 1) + dis(n, n));

		ans = min(ans, dis(1, 1) + cal(n, 0) + cal(0, n));
		ans = min(ans, dis(1, n) + cal(n, 0) + cal(0, 1));
		ans = min(ans, dis(n, 1) + cal(1, 0) + cal(0, n));
		ans = min(ans, dis(n, n) + cal(0, 1) + cal(1, 0));

		ans = min(ans, cal(1, 0) + cal(0, 1) + cal(n, 0) + cal(0, n));
		cout << ans << '\n';
	}
    
    return 0;
}

D

题意

给出 n n n个点,对于每个点,找到一个离该点最近(曼哈顿距离)的且不在着n个点中的点。

思路

  • 从答案开始搜
  • 将离每个点曼哈顿为 1 1 1的点(不在这 n n n个点中)压入队列,进行 B F S BFS BFS,扫到属于 n n n个点中的点就可以确定这个点的答案了。

AC代码

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

	int n;
	cin >> n;
	map<pair<int, int>, int> rk;
	set<pair<int, int> > st;
	vector<int> x(n + 1), y(n + 1);
	vector<pair<int, int> > ans(n + 1);
	for(int i = 1;i <= n;i++) {
		cin >> x[i] >> y[i];
		rk[pair<int, int>(x[i], y[i])] = i;
	}
	for(int i = 1;i <= n;i++) {
		st.insert({x[i] + 1, y[i]});
		st.insert({x[i] - 1, y[i]});
		st.insert({x[i], y[i] + 1});
		st.insert({x[i], y[i] - 1});
	}
	queue<array<int, 4> > q;
	for(auto [x, y] : st) {
		if(rk.count({x, y}) == 0) {
			q.push({x, y, x, y});
		}
	}
	while(q.size()) {
		auto [x, y, ax, ay] = q.front();
		q.pop();
		for(int i = 0;i < 4;i++) {
			int nx = x + dx[i];
			int ny = y + dy[i];
			if(rk.count({nx, ny})) {
				ans[rk[pair<int, int>(nx, ny)]] = {ax, ay};
				rk.erase(pair<int, int>(nx, ny));
				q.push({nx, ny, ax, ay});
			}
		}
	}
	for(int i = 1;i <= n;i++) cout << ans[i].first << ' ' << ans[i].second << '\n';

    return 0;
}

总结

前三道题开的还是很顺利,第四题想了一个很逆天难写的思路,和队友写了一个小时,最后wa2,还是太菜了,不摆了,写题!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值