AtCoder Beginner Contest 236 题解
AtCoder Beginner Contest 236
目前补到F
A - chukodai
题目描述:给你一个字符串\(s\),下标从1
开始,再给你两个整数a , b
,将字符串中位置a
和位置b
的字符交换位置并输出交换后的字符串。
思路:根据题意模拟即可
时间复杂度:\(O(n)\)
参考代码:
void solve() {
string s;
cin >> s;
int a, b;
cin >> a >> b;
swap(s[a - 1], s[b - 1]);
cout << s << '\n';
}
B - Who is missing?
题目描述:给你\(4 * n - 1\)张卡片,每张卡片上有一个\(1 \sim n\)之间的数字,同一数字最多出现4
次,有一个数字只出现了三次,请将这个数字找出来。
思路:根据题意模拟即可
时间复杂度:\(O(n)\)
参考代码:
void solve() {
int n;
cin >> n;
vector<int>cnt(n + 1, 0);
int a;
for (int i = 1; i < 4 * n; ++i) cin >> a, cnt[a]++;
for (int i = 1; i <= n; ++i) {
if (cnt[i] == 3) {
cout << i << '\n';
return;
}
}
return;
}
C - Route Map
题目描述:给你两个集合\(S , T\),问\(S\)的每个元素是否在\(T\)中出现过,对于每个元素,若出现过输出Yes
,否则输出No
。
思路:使用set
或map
等数据结构存储\(T\)中的元素,然后遍历\(S\)中的元素在使用的数据结构中查找,根据查找结果输出相应的答案即可。
时间复杂度:\(O(nlogn)\)
参考代码:
void solve() {
int n, m;
cin >> n >> m;
vector<string>strs(n);
for (int i = 0; i < n; ++i) cin >> strs[i];
map<string, bool>mp;
string s;
for (int i = 1; i <= m; ++i) {
cin >> s;
mp[s] = true;
}
for (auto s : strs) {
if (mp.count(s)) cout << "Yes" << '\n';
else cout << "No" << '\n';
}
return;
}
D - Dance
题目描述:有\(2 * n\)个人,让你将其分成\(n\)组,每组中的两个人会有一个匹配值\(B_{i , j}\)其中i , j
表示人的编号。问对于所有的分组方案,每一组的匹配值的异或和的最大值是多少。假设每组的匹配值为\(A_i\),即求\(A_1 \oplus A_2 \oplus ... \oplus A_n\)的最大值。
数据范围:\(1 \leq n \leq 8 , 0 \leq B_{i , j} < 2^{30}\)
思路:显然可以爆搜,因为要配对,如果当前已经枚举的人数为偶数,那么任选一个人作为下一组即可,若当前已经分配好的是奇数个,那么就要遍历剩下的人与前一个人组成一队。
时间复杂度:\(O((2 * n - 1)!!)\)
参考代码:
void solve() {
int n;
cin >> n;
vector<vector<int>>a(2* n + 1, vector<int>(2 * n + 1, 0));
for (int i = 1; i <= 2 * n - 1; ++i) {
for (int j = i + 1; j <= 2 * n; ++j) cin >> a[i][j];
}
int res = 0, m = n << 1;
vector<bool>vis(2 * n + 1, false);
auto dfs = [&](auto dfs, int cur, int cnt, int ans) {
if (cnt == m) {
res = max(res, ans);
return;
}
if (cnt % 2 == 0) {
for (int i = 1; i <= m; ++i) {
if (vis[i] == true) continue;
vis[i] = true;
dfs(dfs, i, cnt + 1, ans);
vis[i] = false;
break;
}
}
else {
for (int i = cur + 1; i <= m; ++i) {
if (vis[i] == true) continue;
vis[i] = true;
int u = cur, v = i;
dfs(dfs, i, cnt + 1, ans ^ a[u][v]);
vis[i] = false;
}
}
};
vis[1] = true;
dfs(dfs, 1, 1, 0);
cout << res << '\n';
return;
}
E - Average and Median
题目描述:给你一个长度为\(n\)的序列\(a\),你需要从中选出一些元素组成序列\(b\),选取规则为:\(\forall 1 \leq i < n - 1\),\(a_{i}\)和\(a_{i - 1}\)至少有一个在序列\(b\)中。求序列\(b\)的均值的最大值和最大中位数。
数据范围:\(2 \leq n \leq 10^5 , 1 \leq a_i \leq 10^9\)
思路:先考虑这样一个问题,若按照题目的选取规则,求选取的和最大。那么显然可以dp
,定义状态\(f_i , g_i\)分别表示不选第\(i\)个数和选第\(i\)个数字,那么转移方程显然为:
因为只与前一项值有关,所以该递推式可以\(O(n)\)时间\(O(1)\)空间求解。
回到原问题:我们可以二分平均值的最大值mid
,那么将每一项a_i-mid
作为c_i
,那么原问题就等价于求按照上述选取规则使得\(b\)序列的和大于等于0
。该过程可以使用上述方法\(O(n)\)求解。故求均值的最大值的复杂度为:\(O(nlog(\mathop{max}\limits_{1\leq i \leq n}\;a_i))\)。
再来看第二个问题,这是一个比较经典的问题,我们同样可以二分最大中位数\(mid\),然后将序列\(a\)数字小于\(mid\)的置为-1
,将数字大于等于\(mid\)的置为1
,那么原问题又转化为按照上述规则选取序列\(a\)中的元素,使得最大和大于0
。该过程使用上述方法同样可以\(O(n)\)求解,总复杂度为\(O(nlogn)\)。
时间复杂度:\(O(nlogn)\)
空间复杂度:\(O(n)\)
参考代码:
template<class T>
T maximize(vector<T>& nums) {
T f = 0, g = 0;
for (auto& num : nums) {
T u = max(f, g) + num;
f = g;
g = u;
}
return max(f, g);
}
void solve() {
int n;
cin >> n;
vector<int>nums(n, 0);
for (auto& num : nums) cin >> num;
auto average = [&]()->void {
double lr = 1.0, rs = 1e9 + 5;
vector<double>b(n);
while ((rs - lr) / rs > 3e-7) {
double mid = sqrt(lr * rs);
for (int i = 0; i < n; ++i) b[i] = nums[i] - mid;
if (maximize(b) >= 0.0) lr = mid;
else rs = mid;
}
cout << fixed << setprecision(10) << lr << '\n';
return;
};
average();
auto median = [&]()->void {
int lr = 1, rs = 1e9 + 5;
vector<int>b(n);
while (lr + 1 < rs) {
int mid = lr + rs >> 1;
for (int i = 0; i < n; ++i) b[i] = nums[i] >= mid ? 1 : -1;
if (maximize(b) > 0) lr = mid;
else rs = mid;
}
cout << lr << '\n';
return;
};
median();
return;
}
F - Spices
题目描述:有\(2^n - 1\)个数字\(1 , 2 , ... , 2^n - 1\),获得一个数字需要花费\(c_i\),你的目的是在这些数字中选择一些数字组成集合\(S\),在集合\(S\)中选择一些数字进行异或和可以得到\(1 \sim 2^n - 1\)之间的所有数字,求最小的花费。
思路:从求集合\(S\)的角度来看,是一个比较裸的线性基,学习线性基的相关知识可以参考此处。要使花费最小,可以将数字和花费绑定并按照花费从小到大排序,然后正常求解即可。
时间复杂度:\(O(2^n log2^n) = O(n \times 2^n)\)
参考代码:
void solve() {
int n, val;
cin >> n;
int m = 1 << n;
vector<pair<int, int>>a;
for (int i = 1; i < m; ++i) {
cin >> val;
a.push_back({ val , i });
}
std::sort(a.begin(), a.end());
vector<int>p(n + 1, 0);
long long res = 0;
for (int i = 1; i < m; ++i) {
int tmp = a[i - 1].second;
for (int j = n; j >= 0; --j) {
if (((tmp >> j) & 1) == 0) continue;
if (p[j] == 0) {
p[j] = tmp;
res += a[i - 1].first;
break;
}
else tmp ^= p[j];
}
}
cout << res << '\n';
return;
}
作者:cherish.
出处:https://home.cnblogs.com/u/cherish-/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。