Japan Registry Services (JPRS) Programming Contest 2023 (AtCoder Beginner Contest 324)
A - Same
这类题目我习惯上将整个数组排序,然后判断首尾元素是否相同。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end());
if (a.front() == a.back()) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
return 0;
}
B - 3-smooth Numbers
实际上就是判断 N N N的质因子是否仅包含2和3,那么我们只需要将 N N N不断除以2和3就好了,最终得到1说明符合题意,否则不符合。
注意千万不要去枚举质数,那样会很慢并且没有意义。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
i64 n;
std::cin >> n;
while (n % 2 == 0) {
n /= 2;
}
while (n % 3 == 0) {
n /= 3;
}
std::cout << (n == 1 ? "Yes\n" : "No\n");
return 0;
}
C - Error Correction
若两串仅有一个字符不同,实际上就仅有三个位置:头、尾、中间。
考虑每个
S
i
S_i
Si字符串和
t
t
t串的最长公共前缀
l
c
p
lcp
lcp和最长公共后缀
l
c
s
lcs
lcs,若两串相等,则
l
c
p
=
=
l
c
s
=
=
t
.
s
i
z
e
(
)
lcp == lcs == t.size()
lcp==lcs==t.size();若
S
i
S_i
Si少一个字符,
l
c
p
+
l
c
s
≥
t
.
s
i
z
e
(
)
−
1
lcp+lcs \ge t.size() - 1
lcp+lcs≥t.size()−1;若
S
i
S_i
Si多一个字符,
l
c
p
+
l
c
s
≥
t
.
s
i
z
e
(
)
lcp+lcs \ge t.size()
lcp+lcs≥t.size();若
S
i
S_{i}
Si与
t
t
t有一个字符不同,则
l
c
p
+
l
c
s
≥
t
.
s
i
z
e
(
)
−
1
lcp+lcs \ge t.size() - 1
lcp+lcs≥t.size()−1。合并一下上述情况其实我们可以写出下面的式子:((s.size() == t.size() && lcp + lcs >= s.size() - 1) || (s.size() == t.size() - 1 && lcp + lcs >= s.size()) || (s.size() - 1 == t.size() && lcp + lcs >= t.size())
,符合这个条件即能给答案贡献1。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::string t;
std::cin >> t;
std::vector<int> ans;
for (int i = 0; i < n; i++) {
std::string s;
std::cin >> s;
int lcp = 0, lcs = 0;
while (lcp < s.size() && lcp < t.size() && s[lcp] == t[lcp]) {
lcp++;
}
while (lcp < s.size() && lcp < t.size() && s[s.size() - 1 - lcs] == t[t.size() - 1 - lcs]) {
lcs++;
}
if ((s.size() == t.size() && lcp + lcs >= s.size() - 1) || (s.size() == t.size() - 1 && lcp + lcs >= s.size())
|| (s.size() - 1 == t.size() && lcp + lcs >= t.size())) {
ans.push_back(i + 1);
}
}
std::cout << ans.size() << "\n";
for (auto x : ans) {
std::cout << x << " ";
}
return 0;
}
D - Square Permutation
由于字符串长度至多为13,那么 N N N最大也就是 1 0 13 10^{13} 1013,我们只需要枚举 0 → 1 0 13 0 \to \sqrt{10^{13}} 0→1013,这些数的平方就好了,枚举每个数的时候考虑原串 S S S中的字符数目是否够用,不够就说明当前枚举的数不符合条件,否则符合条件,贡献+1。
#include <bits/stdc++.h>
using i64 = long long;
constexpr int N = 3170000;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::string s;
std::cin >> s;
std::vector<int> cnt(10);
for (auto c : s) {
cnt[c - '0']++;
}
int ans = 0;
for (int i = 0; i < N; i++) {
auto backup = cnt;
i64 v = 1LL * i * i;
std::string t = std::to_string(v);
if (t.size() > n) {
break;
}
t = std::string(n - t.size(), '0') + t;
bool tag = true;
for (int j = 0; j < n; j++) {
if (backup[t[j] - '0'] == 0) {
tag = false;
}
backup[t[j] - '0']--;
}
ans += tag;
}
std::cout << ans << "\n";
return 0;
}
E - Joint Two Strings
两个串拼起来包含一个子串,其实就是前面的串包含这个子串的一部分,后面的串包含这个子串的另一部分。那么我们仍然可以从前缀和后缀的角度来考虑,若前串和后串对于子串的前缀和后缀的包含情况的加和大于等于这个子串的话说明这两个串拼接起来之后包含这个字串。后面统计方案数可以双指针也可以二分。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
std::cin >> n;
std::string t;
std::cin >> t;
std::vector<int> pre(n), suf(n);
for (int i = 0; i < n; i++) {
std::string s;
std::cin >> s;
int len = 0;
for (auto c : s) {
if (c == t[len]) {
len++;
}
}
pre[i] = len;
len = 0;
for (auto c : std::ranges::views::reverse(s)) {
if (c == t[t.size() - 1 - len]) {
len++;
}
}
suf[i] = len;
}
std::sort(pre.begin(), pre.end());
std::sort(suf.begin(), suf.end());
// for (int i = 0; i < n; i++) {
// std::cout << pre[i] << " " << suf[i] << "\n";
// }
i64 ans = 0;
for (auto x : pre) {
auto p = std::lower_bound(suf.begin(), suf.end(), t.size() - x);
ans += suf.end() - p;
}
std::cout << ans << "\n";
return 0;
}
F - Beautiful Path
诡异的分数问题求值可以考虑二分。
我们考察本题的式子:题目要求 ∑ b ∑ c \frac{\sum{b}}{\sum{c}} ∑c∑b最大,我们设 ∑ b ∑ c ≥ t \frac{\sum{b}}{\sum{c}} \ge t ∑c∑b≥t,若这个式子合法,那么有 ∑ b ≥ t × ∑ c \sum{b} \ge t \times \sum{c} ∑b≥t×∑c,等价于 ∑ t c − ∑ b ≤ 0 \sum{tc} - \sum{b} \le 0 ∑tc−∑b≤0,也即 ∑ ( t c − b ) ≤ 0 \sum{(tc-b)} \le 0 ∑(tc−b)≤0,因此实际上当前图上每一条边权就是 t c − b tc-b tc−b,如果满足条件说明可以以此作为答案下限,否则作为答案上限。
如何求这个式子的最小值呢?由于题目保证边是由较小点连向较大点,所以我们可以直接在有向无环图上DP,求出 ∑ ( t c − b ) ≤ 0 \sum{(tc-b)} \le 0 ∑(tc−b)≤0的最小值判断其作为上限还是下限即可。
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
std::cin >> n >> m;
std::vector<std::vector<std::array<int, 3>>> adj(n);
for (int i = 0; i < m; i++) {
int u, v, b, c;
std::cin >> u >> v >> b >> c;
u--, v--;
adj[u].push_back({v, b, c});
}
double lo = 0, hi = 1E9;
for (int t = 0; t <= 100; t++) {
double x = (lo + hi) / 2;
std::vector<double> dp(n, INFINITY);
dp[0] = .0;
for (int i = 0; i < n; i++) {
for (auto [j, b, c] : adj[i]) {
dp[j] = std::min(dp[j], dp[i] + x * c - b);
}
}
// for (int i = 0; i < n; i++) {
// std::cout << dp[i] << " \n"[i == n - 1];
// }
if (dp.back() <= 0) {
lo = x;
} else {
hi = x;
}
}
std::cout << std::fixed << std::setprecision(12);
std::cout << lo << "\n";
return 0;
}