ABC393题解
AtCoder Beginner Contest 393
A
算法标签: 模拟
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
string s1, s2;
cin >> s1 >> s2;
if (s1 == "sick" && s2 == "fine") cout << 2 << "\n";
else if (s1 == "fine" && s2 == "sick") cout << 3 << "\n";
else if (s1 == "fine" && s2 == "fine") cout << 4 << "\n";
else cout << 1 << "\n";
return 0;
}
B
算法标签: 模拟
思路
每次找到BBB的位置, 以BBB为中心向两侧扩展, 然后删除该位置
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
string s;
cin >> s;
int res = 0;
while (s.find('B') != -1) {
int u = s.find('B');
int n = s.size();
for (int i = 1; i <= n; ++i) {
if (u - i < 0 || u + i >= n) continue;
if (s[u - i] == 'A' && s[u + i] == 'C') res++;
}
s[u] = ' ';
}
cout << res << "\n";
return 0;
}
C
算法标签: 哈希映射, 哈希表
思路
注意计算哈希值的时候转化成long longlong \, longlonglong
#include <iostream>
#include <algorithm>
#include <cstring>
#include <unordered_set>
#include <utility>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10, M = 5e5 + 10;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
unordered_set<LL> s;
int res = 0;
while (m--) {
int u, v;
cin >> u >> v;
if (u == v) {
res++;
continue;
}
LL hash1 = (LL)u * N + v;
LL hash2 = (LL)v * N + u;
if (!s.count(hash1) && !s.count(hash2)) {
s.insert(hash1);
s.insert(hash2);
}
else res++;
}
cout << res << "\n";
return 0;
}
D
算法标签: 贪心, 模拟
思路
因为最后一定是一段111连接到一起, 因此设111的数量是cntcntcnt, 最后111的起始位置lll, 那么终点坐标就是l+cnt−1l + cnt - 1l+cnt−1, 设初始时每个111的位置是aia_iai, 那么目标就是最小化
ans=min{∑i=1n∣ai−(l+i−1)∣} ans =\min\left \{ \sum _{i = 1} ^ {n} |a_i - (l + i - 1)| \right \} ans=min{i=1∑n∣ai−(l+i−1)∣}
整理后得到
ans=min{∑i=1n∣ai−i−(l−1)∣} ans =\min\left \{ \sum _{i = 1} ^ {n} |a_i - i - (l - 1)| \right \} ans=min{i=1∑n∣ai−i−(l−1)∣}
设bi=ai−ib_i = a_i - ibi=ai−i, x=l−1x = l - 1x=l−1, 那么原式变为
ans=min{∑i=1n∣bi−x∣} ans =\min\left \{ \sum _{i = 1} ^ {n} |b_i - x| \right \} ans=min{i=1∑n∣bi−x∣}
xxx取中位数就是最小值
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int n;
string s;
vector<int> nums;
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> s;
int k = 1;
for (int i = 0; i < n; ++i) {
if (s[i] == '1') {
nums.push_back(i - k);
k++;
}
}
sort(nums.begin(), nums.end());
int x = nums[nums.size() / 2];
LL res = 0;
for (int val : nums) res += abs(val - x);
cout << res << "\n";
return 0;
}
E
算法标签: 倍数, 数论, gcdgcdgcd, 调和级数
思路
首先看AiA_iAi的最大值范围不超过10610 ^ 6106, 也就是答案gcd≤106gcd \le 10 ^ 6gcd≤106, 因此可以枚举gcdgcdgcd, 题目要求kkk个元素中必须包含AiA_iAi, 设d=gcdd = gcdd=gcd, 必须满足d∣Aid | A_id∣Ai, 并且在剩余元素中至少有k−1k - 1k−1个ddd的倍数
设cnt[x]cnt[x]cnt[x]表示AAA中xxx出现的次数, mul[x]mul[x]mul[x]表示AAA中xxx倍数出现的次数, 那么有mul[x]=cnt[x]+cnt[2x]+cnt[3x]+...+cnt[nx]mul[x] = cnt[x] + cnt[2x] + cnt[3x] + ... + cnt[nx]mul[x]=cnt[x]+cnt[2x]+cnt[3x]+...+cnt[nx], 调和级数枚举每个xxx的倍数, 时间复杂度O(nlnn)O(n \ln n)O(nlnn), 可以放缩为O(nlogn)O(n \log n)O(nlogn)
算法过程
- 首先枚举ddd
- 然后检查mul[d]≥kmul[d] \ge kmul[d]≥k, 如果满足条件, 更新ddd的倍数的AiA_iAi的答案
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1.2E6 + 10, M = 1e6 + 10;
int n, k;
int w[N], cnt[N], mul[N];
int res[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> w[i];
cnt[w[i]]++;
}
// 调和级数枚举所有数倍数出现的次数
for (int i = 1; i < M; ++i) {
for (int j = i; j < M; j += i) {
mul[i] += cnt[j];
}
}
for (int d = 1; d < M; ++d) {
if (mul[d] < k) continue;
for (int x = d; x < N; x += d) {
res[x] = max(res[x], d);
}
}
for (int i = 1; i <= n; ++i) cout << res[w[i]] << "\n";
return 0;
}
F
算法标签: 离线, 树状数组, 离散化, 动态规划, LISLISLIS
思路
注意到, 查询数量和序列长度都是2×1052 \times 10 ^ 52×105, O(n2)O(n ^ 2)O(n2)的直接做法会超时
LISLISLIS问题有一个树状数组优化做法, 设f[j]f[j]f[j]表示以jjj结尾的最长上升子序列的长度, 更新方式f[a[j]]=maxk<a[i]{f[k]}+1f[a[j]] = \max _{k < a[i]} \left \{ f[k] \right \} + 1f[a[j]]=maxk<a[i]{f[k]}+1, 取maxmaxmax的过程可以使用树状数组优化
树状数组求最长上升子序列问题
树状数组求LISLISLIS
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1010;
int n, w[N];
vector<int> nums;
int tr[N];
int get(int val) {
return lower_bound(nums.begin(), nums.end(), val) - nums.begin() + 1;
}
void disc() {
for (int i = 1; i <= n; ++i) nums.push_back(w[i]);
sort(nums.begin(), nums.end());
for (int i = 1; i <= n; ++i) w[i] = get(w[i]);
}
int lowbit(int val) {
return val & -val;
}
void update(int u, int val) {
for (int i = u; i < N; i += lowbit(i)) tr[i] = max(tr[i], val);
}
int get_max(int u) {
int res = 0;
for (int i = u; i; i -= lowbit(i)) res = max(res, tr[i]);
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
disc();
int res = 1;
for (int i = 1; i <= n; ++i) {
int curr = get_max(w[i] - 1) + 1;
res = max(res, curr);
update(w[i], curr);
}
cout << res << "\n";
return 0;
}
因为当前问题是离线问题, 因此可以将查询顺序打乱, 考虑将查询按照RRR从小到大排序依次处理, 每次处理完a[i]a[i]a[i]后计算R=iR = iR=i的所有询问, 时间复杂度O((N+Q)logM)O((N + Q) log M)O((N+Q)logM), MMM是树状数组大小
树状数组做法
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 2e5 + 10;
int n, q;
int w[N];
struct Query {
int x, id;
};
vector<Query> qs[N];
int tr[N << 1], ans[N];
// 离散化
void disc() {
vector<int> nums;
for (int i = 1; i <= n; ++i) nums.push_back(w[i]);
for (int i = 1; i <= n; ++i) {
for (auto [x, id] : qs[i]) {
nums.push_back(x);
}
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
for (int i = 1; i <= n; ++i) {
w[i] = lower_bound(nums.begin(), nums.end(), w[i]) - nums.begin() + 1;
}
for (int i = 1; i <= n; ++i) {
for (Query &q : qs[i]) {
q.x = lower_bound(nums.begin(), nums.end(), q.x) - nums.begin() + 1;
}
}
}
int lowbit(int val) {
return val & -val;
}
void update(int u, int val) {
for (int i = u; i < N * 2; i += lowbit(i)) {
tr[i] = max(tr[i], val);
}
}
int get_max(int u) {
int res = 0;
for (int i = u; i > 0; i -= lowbit(i)) {
res = max(res, tr[i]);
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = 1; i <= q; ++i) {
int r, x;
cin >> r >> x;
// 记录最大值范围和查询id
qs[r].push_back({x, i});
}
disc();
for (int i = 1; i <= n; ++i) {
int curr = get_max(w[i] - 1) + 1;
update(w[i], curr);
for (Query q : qs[i]) {
ans[q.id] = get_max(q.x);
}
}
for (int i = 1; i <= q; ++i) cout << ans[i] << "\n";
return 0;
}
动态规划 + 二分做法
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 2e5 + 10;
int n, q, w[N];
struct Query {
int r, x, id;
int ans;
} qs[N];
bool cmp_r(Query &q1, Query &q2) {
return q1.r < q2.r;
}
bool cmp_id(Query &q1, Query &q2) {
return q1.id < q2.id;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> q;
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = 1; i <= q; ++i) {
int r, x;
cin >> r >> x;
qs[i] = {r, x, i};
}
sort(qs + 1, qs + q + 1, cmp_r);
vector<int> f;
f.push_back(0);
int qi = 1;
for (int i = 1; i <= n; ++i) {
// 第一个大于等于w[i]的位置
auto it = lower_bound(f.begin(), f.end(), w[i]);
if (it == f.end()) f.push_back(w[i]);
else *it = w[i];
while (qi <= q && qs[qi].r == i) {
qs[qi].ans = upper_bound(f.begin(), f.end(), qs[qi].x) - f.begin() - 1;
qi++;
}
}
sort(qs + 1, qs + q + 1, cmp_id);
for (int i = 1; i <= q; ++i) cout << qs[i].ans << "\n";
return 0;
}
G
先鸽, 因为是黑题, 有时间补上
更多推荐
所有评论(0)