Codeforces Round #650 (Div. 3)题解

Codeforces Round #650 (Div. 3)

A.Short Substrings

题目大意

设原有的字符串为s,现在给你由s转化而来的t,让你输出原有的字符串s
其中t = s[0]s[1]s[1]s[2]s[2]s[3]…s[n - 1]s[n - 1]s[n]

解题思路

容易得到s = t[i % 2 == 0] + t[t.length() - 1]

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		string s;
		cin >> s;
		int l = s.length();
		for (int i = 0; i < l; ++i) {
			if (i % 2 == 0) cout << s[i];
		}
		cout << s[l - 1] << '\n';
	}
	return 0;
}

B.Even Array

题目大意

给你一个长度为n的数组a,你可以将数组中的任意两个数进行交换,问你最少交换多少次可以使得任意的i有a[i] % 2 == i % 2,如果不能满足那么输出-1

解题思路

显然需要交换的是a[i] % 2 != i % 2的数,统计其中奇数和偶数的个数,如果相等则最小次数为奇数个数,否则输出-1

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int a[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		int n;
		cin >> n;
		for (int i = 0; i < n; ++i) cin >> a[i];
		int x = 0, y = 0;
		for (int i = 0; i < n; ++i) {
			if (a[i] % 2 == i % 2) continue;
			else {
				if (a[i] % 2 == 0) ++x;
				else ++y;
			}
		}
		if (x == y) cout << x << '\n';
		else cout << "-1\n";
	}
	return 0;
}

C.Social Distance

题目大意

给你一个由0和1组成的字符串,每两个1之间的0不得小于k,问你可以用1替换0的最大个数是多少

解题思路

首先,考虑最特殊的情况1,如果没有1,那么使得第一个字符为1,然后其余的n - 1个字符每k + 1个可以有一个1,那么答案则为(n - 1) / (k + 1) + 1
然后考虑一般的情况,,统计每两个1之间的0的个数,然后减去2 * (k - 1),因为与这两个1相邻的2 * (k - 1)个0不能有1,对答案没有贡献,那么剩余的0即为情况1
最后,考虑最前面的0和最后面的0,同样可以将其转换为情况1求解

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		int n, k;
		cin >> n >> k;
		++k;
		string s;
		cin >> s;
		vector<int> v;
		for (int i = 0; i < n; ++i) {
			if (s[i] - '0' == 1) v.push_back(i);
		}
		int len = v.size();
		int res = 0;
		for (int i = 1; i < len; ++i) {
			int tmp = v[i] - v[i - 1] - 2 * k + 1;
			if (tmp <= 0) continue;
			res += ((tmp - 1) / k) + 1;
		}
		if (!len) {
			res += ((n - 1) / k) + 1;
		}
		else if (len) {
			res += max(v[0] / k, 0) + max((n - v[len - 1] - 1) / k, 0);
		}
		cout << res << '\n';
	}
	return 0;
}

D.Task On The Board

题目大意

给你一个字符串s,将s删去一些字符并重排列后得到字符串t,构造数组b,b[i] = sum(abs(i - j)),其中i和j满足t[j] > t[i],告诉你t的长度m和由t构造的b数组,让你求出任意t满足上述条件

解题思路

首先容易想到,b数组中为0的一定字母序是最大的,记录这些下标j,然后将其余位置i的数减去abs(i - j),那么一定又会得到一些0,那么这就是一个子问题求解,最后按照字母序相对大小到s中找到相应的字母即可

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int a[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	register int T;
	cin >> T;
	while (T--) {
		string s;
		cin >> s;
		int m;
		cin >> m;
		for (register int i = 0; i < m; ++i) {
			cin >> a[i];
		}
		int b[50] = { 0 }, c[50] = { 0 };
		for (int i = 0; i < s.length(); ++i) {
			++b[s[i] - 'a'];
		}
		bool vis[110] = { false };
		string t = s.substr(0, m);
		char cc = 'z';
		vector<int> v[30];
		for (int i = 0; i < m; ++i) {
			for (int j = 0; j < m; ++j) {
				if (vis[j]) continue;
				if (!a[j]) {
					vis[j] = true;
					v[cc - 'a'].push_back(j);
					++c[cc - 'a'];
				}
			}
			for (int j = 0; j < m; ++j) {
				if (vis[j]) continue;
				for (int k = 0; k < v[cc - 'a'].size(); ++k) {
					a[j] -= abs(j - v[cc - 'a'][k]);
				}
			}
			cc = cc - 1;
		}
		int cnt = 'z' - 'a' + 1;
		for (int i = 'z' - 'a'; i >= 0; --i) {
			if (!v[i].size()) continue;
			for (int j = cnt - 1; j >= 0; --j) {
				if (c[i] <= b[j]) {
					cnt = j;
					break;
				}
			}
			for (int j = 0; j < v[i].size(); ++j) {
				t[v[i][j]] = cnt + 'a';
			}
		}
		cout << t << '\n';
	}
	return 0;
}

E.Necklace Assembly

题目大意

给你一个字符串s,让你从其中尽可能多的挑选一些字符组成一串项链,要求将其顺时针旋转(下标右移)k次后与原项链相同

解题思路

容易想到项链中存在循环节
假设项链长度为L,循环节长度为circle_len,循环节个数为circle_num,则有:
L % circle_len == 0
k % circle_len == 0
circle_num = L / circle_len
那么L固定的情况下,循环节长度越大,循环节个数越少,就可以让更多的s中出现次数大于循环节个数的字符出现在项链中,而要满足上述要求,max(circle_len) = gcd(L, k)
设a[i]为字符i在s中出现的次数,那么字符i在该项链的一个循环节中可以出现a[i] / circle_num次
如果sum(a[i] / circle_num) * circle_num >= L,则说明长度L满足要求。
由于L最大为s.length(),那么从小到大遍历可行的L即可

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
inline int gcd(int x, int y) {
	return y == 0 ? x : gcd(y, x % y);
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		int n, k;
		cin >> n >> k;
		string s;
		cin >> s;
		int a[50] = { 0 };
		for (int i = 0; i < n; ++i) {
			++a[s[i] - 'a'];
		}
		int res = 0;
		for (int i = 1; i <= n; ++i) {
			int circle_len = gcd(i, k);
			int circle_num = i / circle_len;
			int tmp_c = 0;
			for (int j = 0; j < 26; ++j) {
				tmp_c += a[j] / circle_num;
			}
			if (tmp_c * circle_num >= i) {
				res = max(res, i);
			}
		}
		cout << res << '\n';
	}
	return 0;
}

F.Flying Sort (Easy Version)

题目大意

给你一个数组a,你可以将任意数字移动到数组开头或结尾,问你最少移动多少次可以使得数字非递减
数组中没有相同的元素,且lengthof(a) <= 3000

解题思路

首先将a数组映射成相对大小(比如4 7 2 3 8,映射后为3 4 1 2 5),然后我们反向思考,看哪些数字不需要移动,发现子序列中连续递增的最大长度即为不需要移动的最大长度,其余元素均需要移动,如上述例子中的3 4 5,只需将1 2移动到最前面即可,那么找到最长的连续递增的子序列Max,答案即为n - Max

AC代码

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int a[maxn], b[maxn];
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while (T--) {
		int n;
		cin >> n;
		for (int i = 1; i <= n; ++i) {
			cin >> a[i];
			b[i] = a[i];
		}
		sort(b + 1, b + n + 1);
		for (int i = 1; i <= n; ++i) {
			a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
		}
		int k = 1, Max = 0;
		while (k <= n) {
			int len = 0;
			for (int i = 1; i <= n; ++i) {
				if (a[i] == k) {
					++k, ++len;
				}
			}
			Max = max(Max, len);
		}
		cout << n - Max << '\n';
	}
	return 0;
}

你可能感兴趣的:(codeforces)