Educational Codeforces Round 134 (Rated for Div. 2)

本文精选了四道算法竞赛题目并提供了详细的题解,包括最小操作数染色问题、最短路径避开激光区域问题、序列变换求解最小最大值问题以及序列重组求最大AND值问题。

A. Image

题意

2 X 2 2X2 2X2 的格子,由 26 26 26 种颜色染色。可以对格子进行操作,每次操作可以选择一个格子染成任意一种颜色或者选择二个格子染成同一种颜色。询问需要的最小操作数使得四个格子颜色相同。

题解

分类讨论
v i s [ x ] vis[x] vis[x]表示有 x x x个格子颜色相同的颜色种类数。

答案情况( v i s [ 1 ] , v i s [ 2 ] , v i s [ 3 ] , v i s [ 4 ] vis[1],vis[2],vis[3],vis[4] vis[1],vis[2],vis[3],vis[4])
0 ( 0 , 0 , 0 , 1 ) (0,0,0,1) (0,0,0,1)
1 ( 2 , 1 , 0 , 0 ) (2,1,0,0) (2,1,0,0)
2 ( 0 , 2 , 0 , 0 ) , ( 1 , 0 , 1 , 0 ) (0,2,0,0),(1,0,1,0) (0,2,0,0),(1,0,1,0)
3 ( 4 , 0 , 0 , 0 ) (4,0,0,0) (4,0,0,0)

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int T;
char s[10][10];

int main() {
	cin >> T;
	while (T--) {
		map<char, int>mp;
		for (int i = 0; i < 2; i++) {
			for (int j = 0; j < 2; j++) {
				char x; cin >> x;
				mp[x]++;
			}
		}
		int vis[10] = { 0,0,0,0,0,0,0,0 };
		for (auto p : mp) {
			vis[p.second]++;
		}
		if (vis[1] == 4) {
			cout << "3\n";
		}
		else if (vis[2] == 2||vis[3]==1) {
			cout << "1\n";
		}
		else if (vis[4] == 1) {
			cout << "0\n";
		}
		else {
			cout << "2\n";
		}
	}
	return 0;
}

B. Deadly Laser

题意

在一个 n ∗ m n * m nm 的矩阵中,与 ( s x , s y ) (s_x,s_y) (sx,sy)的曼哈顿距离小于等于 d d d的格子不能走,请问从 ( 1 , 1 ) (1,1) (1,1)移动到 ( n , m ) (n,m) (n,m) 的最短距离。

题解

ZvItx.png

如图所示,我们会发现不能走的区域是一个正方形,并且只要这个正方形的任意一条边不与矩阵相交,那么就可以以最短距离到达终点。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int T;
int n, m, sx, sy, d;

int main() {
	cin >> T;
	while (T--) {
		cin >> n >> m >> sx >> sy >> d;
		if ((sx - d <= 1 && sx + d >= n) || (sy - d <= 1 && sy + d >= m) || (sx - d <= 1 && sy - d <= 1) || (sx + d >= n && sy + d >= m))cout << "-1\n";
		else cout << n + m - 2 << "\n";
	}
	return 0;
}

C. Min-Max Array Transformation

题意

有三个序列 a , b , d a,b,d a,b,d,其中 a i + d i = b i a_i+d_i=b_i ai+di=bi

给出序列 a , b a,b a,b,序列可以重新排列,询问每个 d i d_i di在重新排列后可以变成的最小值和最大值。

题解

首先,考虑如何求最小值。

序列已经按从小到大排好序,且 a i ≤ b i a_i\leq b_i aibi

a 1     a 2     a 3     a x     a n a_1~~~a_2~~~a_3~~~ a_x~~~a_n a1   a2   a3   ax   an

b 1     b 2     b 3     b x     b n b_1~~~b_2~~~b_3~~~ b_x~~~b_n b1   b2   b3   bx   bn

由于我们需要求 a x + d m i n = b y a_x + d_{min}=b_y ax+dmin=by,那么我们只需要找到大于等于 a x a_x ax的第一个数,则 d m i n d_{min} dmin最小。

那么这样求,会不会使其他位置的 d i d_i di 不满足题设呢?假设 y = 2 y=2 y=2,序列会变成

a 1     a x     a 2     a 3     a n a_1~~~a_x~~~a_2~~~ a_3~~~a_n a1   ax   a2   a3   an

b 1     b 2     b 3     b x     b n b_1~~~b_2~~~b_3~~~ b_x~~~b_n b1   b2   b3   bx   bn

仍然满足 a i ≤ b i a_i\leq b_i aibi的条件。

接下来,我们来考虑求最大值的情况。

a 1     a 2     a 3     a 4     a 5 a_1~~~a_2~~~a_3~~~ a_4~~~a_5 a1   a2   a3   a4   a5

b 1     b 2     b 3     b 4     b 5 b_1~~~b_2~~~b_3~~~ b_4~~~b_5 b1   b2   b3   b4   b5

对于 a 1 a_1 a1,如果我们想让 d 1 d_1 d1 尽量大,那么应该选择 d 1 = b 5 − a 1 d_1=b_5-a_1 d1=b5a1,那么序列就会变成

a 2     a 3     a 4     a 5     a 1 a_2~~~a_3~~~ a_4~~~a_5~~~a_1 a2   a3   a4   a5   a1

b 1     b 2     b 3     b 4     b 5 b_1~~~b_2~~~b_3~~~ b_4~~~b_5 b1   b2   b3   b4   b5

此时, a 5 a_5 a5有可能会大于 b 4 b_4 b4,导致题设不成立。

因此我们应该选择第一个 a i + 1 ≤ b i a_{i+1} \leq b_i ai+1bi 使得题设成立。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int T, n;
int a[200010], b[200010];

int main() {
	cin >> T;
	while (T--) {
		cin >> n;
		for (int i = 1; i <= n; i++)cin >> a[i];
		for (int i = 1; i <= n; i++)cin >> b[i];

		vector<int>vec;
		b[0] = 1000000010;
		for (int i = 1; i <= n; i++) {
			int pos = lower_bound(b + 1, b + 1 + n, a[i]) - b;
			cout << b[pos] - a[i] << " ";
			if (a[i] > b[i - 1])vec.push_back(i - 1);
		}
		cout << "\n";
		vec.push_back(n);
		int t = 0;
		for (int i = 1; i <= n; i++) {
			if (i <= vec[t])cout << b[vec[t]] - a[i] << " ";
			if (i == vec[t])t++;
		}
		cout << "\n";
	}
	return 0;
}

D. Maximum AND

题意

有序列 a , b , c a,b,c a,b,c,其中 c i = a i ⨁ b i c_i=a_i \bigoplus b_i ci=aibi,询问对序列 a , b a,b a,b 重新排列后,求 c 1 & c 2 & . . . . . c n c_1 {\&} c_2 {\&}.....c_n c1&c2&.....cn 的最大值。

题解

一些胡说八道:
憨憨原来写的是DFS,因为根据数据范围确实可行,类似于分治递归,判断某一个二进制位是否可取。
后来发现要改成BFS,然后MLE了QAQ,不过好像 dls 也是写的BFS。
在群里看 jls 讲他的做法,发现自己写的好复杂。
后面讲的就是 jls 的做法。

考虑异或以及与的性质,这道题本质上就是找到一个 x x x 使得 a & x = ( 取反  b ) & x a {\&} x = (取反~b) {\&} x a&x=(取反 b)&x

由于需要找到最大的 x x x,我们可以贪心的从最高位开始考虑,若当前位满足条件,保留当前位的同时判断下一位是否符合要求。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int T, n;
ll ans = 0;

bool ok(vector<int>a,vector<int>b,int x) {
	vector<int>c(n), d(n);
	for (int i = 0; i < n; i++) {
		c[i] = a[i] & x;
		d[i] = ~b[i] & x;
	}
	sort(c.begin(), c.end());
	sort(d.begin(), d.end());
	return c == d;
}

int main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> T;
	while (T--) {
		cin >> n; ans = 0;
		vector<int>a(n), b(n);
		for (int i = 0; i < n; i++)cin >> a[i];
		for (int i = 0; i < n; i++)cin >> b[i];
		int ans = 0;
		for (int i = 29; i >= 0; i--) {
			if (ok(a, b, ans | (1 << i))) {
				ans |= 1 << i;
			}
		}
		cout << ans << "\n";
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值