ABC350 A-E题解
下面的内容不包括题目翻译,要想获取题目翻译,请参照 这篇教程 来获取题目翻译。
A
题目
只需要判断
S
S
S 是否在 ABC001
到 ABC349
范围内即可。
可以使用字符串之间的大小关系来快速通过此题。
AC Code(CPP):
#include <iostream>
using namespace std;
string s;
string t = "ABC350";
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s;
if (s < t && s != "ABC316" && s >= "ABC001") {
cout << "Yes";
}
else {
cout << "No";
}
return 0;
}
AC Code(Python):
s = input()
print(('No', 'Yes')[s != 'ABC316' and 'ABC001' <= s <= 'ABC349'])
B
题目
在 C++
中,可以通过使用一个布尔数组来判断这颗牙齿是否被拔除。而在 Python
中,可以通过集合实现。如果集合里面有这个元素,说明这颗牙齿被拔掉了,否则,说明这颗牙齿还在。
AC Code(CPP):
#include <iostream>
using namespace std;
int n, q;
bool vis[2010];
int main() {
cin >> n >> q;
while (q--) {
int t;
cin >> t;
if (vis[t]) {
n++;
vis[t] = 0;
}
else {
n--;
vis[t] = 1;
}
}
cout << n << '\n';
return 0;
}
AC Code(Python):
n, q = [int(i)for i in input().split()]
a = set([])
t = [int(i)for i in input().split()]
for k in t:
if k in a:
n += 1
a.remove(k)
else:
n -= 1
a.add(k)
print(n)
C
题目
答案的特性: i = a i i=a_i i=ai。根据这个特性,我们可以对于不在对应位置上的每一个数,让正确的数和它交换。由于数一共只有 N N N 个,交换最多 N − 1 N-1 N−1 次,所以可以符合条件。
注意 python
的下标问题。
AC Code(CPP):
#include <iostream>
#include <queue>
using namespace std;
int n;
int a[200100];
int p = -1;
int idx[200100];
queue<pair<int, int>> q;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
idx[a[i]] = i;
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (a[i] != i) {
cnt++;
q.push({min(idx[i], i), max(idx[i], i)});
int tmp = idx[i];
swap(idx[a[i]], idx[i]);
swap(a[i], a[tmp]);
}
}
cout << cnt << '\n';
while (!q.empty()) {
cout << q.front().first << ' ' << q.front().second << '\n';
q.pop();
}
return 0;
}
AC Code(Python)
n = int(input())
a = [int(i) - 1 for i in input().split()]
idx = [0 for i in range(n)]
for i in range(n):
idx[a[i]] = i
ans = []
for i in range(n):
if a[i] != i:
ans.append([min(idx[i], i), max(idx[i], i)])
temp = idx[i]
idx[a[i]], idx[i] = idx[i], idx[a[i]]
a[i], a[temp] = a[temp], a[i]
print(len(ans))
for i in ans:
print('%d %d' % (i[0] + 1, i[1] + 1))
D
题目
这个问题须要一点图论知识。
我们可以发现,对于一群人,如果这群人是“联通”的,那么就一定可以将这一群人操作成一个“完全图”。所以对于一群联通的人,操作次数就等于这张图成为完全图的边数(点数乘以点数减一)减去已有的朋友关系。
通过并查集来查找联通块,并快速地解决这个问题。
注意 Python
中递归层数的限制
AC Code(CPP):
#include <iostream>
#define int long long
using namespace std;
int n, m;
int u[200100], v[200100];
int f[200100];
long long cnt[200100], cnt1[200100];
int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
bool vis[200100];
long long ans;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 1; i <= m; i++) {
cin >> u[i] >> v[i];
f[find(v[i])] = find(u[i]);
}
for (int i = 1; i <= m; i++) {
cnt[find(u[i])]++;
}
for (int i = 1; i <= n; i++) {
cnt1[find(i)]++;
}
for (int i = 1; i <= n; i++) {
if (find(i) == i)
ans += cnt1[i] * (cnt1[i] - 1ll) / 2ll - cnt[i];
}
cout << ans << '\n';
return 0;
}
AC Code(Python):
import sys
sys.setrecursionlimit(998244353)
n, m = [int(i) for i in input().split()]
f = [i for i in range(n + 3)]
def find(x):
if x != f[x]:
f[x] = find(f[x])
return f[x]
a = []
for i in range(m):
a.append([int(i) for i in input().split()])
a[i][1] -= 1
a[i][0] -= 1
f[find(a[i][1])] = find(a[i][0])
cnt = [0] * (n + 3)
cnt1 = [0] * (n + 3)
for i in range(m):
cnt[find(a[i][0])] += 1
for i in range(n):
cnt1[find(i)] += 1
ans = 0
for i in range(n):
if find(i) == i:
ans += (cnt1[i] * (cnt1[i] - 1) // 2) - cnt[i]
print(ans)
E
题目
可以想到使用搜索加上记忆化来解决这个问题。设 f ( x ) f(x) f(x) 是把 x x x 变为 0 0 0 的代价,这个值为操作一期望代价和操作二期望代价的最小值,其中操作一期望代价很好算,就是 f ( ⌊ x A ⌋ ) + X f(\lfloor \frac{x}{A} \rfloor) + X f(⌊Ax⌋)+X。而操作二期望代价是这个式子:
f ( x ) = 1 6 f ( ⌊ 1 x ⌋ ) + 1 6 f ( ⌊ x 2 ⌋ ) + 1 6 f ( ⌊ x 3 ⌋ ) + 1 6 f ( ⌊ x 4 ⌋ ) + 1 6 f ( ⌊ x 5 ⌋ ) + 1 6 f ( ⌊ x 6 ⌋ ) + Y f(x)=\frac16f(\lfloor \frac1x \rfloor) + \frac16f(\lfloor \frac x2 \rfloor) + \frac16f(\lfloor \frac x3 \rfloor) + \frac16f(\lfloor \frac x4 \rfloor) + \frac16f(\lfloor \frac x5 \rfloor) + \frac16f(\lfloor \frac x6 \rfloor)+Y f(x)=61f(⌊x1⌋)+61f(⌊2x⌋)+61f(⌊3x⌋)+61f(⌊4x⌋)+61f(⌊5x⌋)+61f(⌊6x⌋)+Y
这个式子看似要无线递归,但是通过移项可以得到这个东西:
f ( x ) = f ( ⌊ x 2 ⌋ ) + f ( ⌊ x 3 ⌋ ) + f ( ⌊ x 4 ⌋ ) + f ( ⌊ x 5 ⌋ ) + f ( ⌊ x 6 ⌋ ) + Y 5 f(x)=\frac{f(\lfloor \frac x2 \rfloor) + f(\lfloor \frac x3 \rfloor) + f(\lfloor \frac x4 \rfloor) + f(\lfloor \frac x5 \rfloor) + f(\lfloor \frac x6 \rfloor)+Y}5 f(x)=5f(⌊2x⌋)+f(⌊3x⌋)+f(⌊4x⌋)+f(⌊5x⌋)+f(⌊6x⌋)+Y
编码就十分简单。
时间复杂度分析
首先,枚举第一种情况是 log A ( N ) \log_A(N) logA(N) 的时间复杂度,可以视为没有。
第二种情况:
由于 ⌊ ⌊ N a ⌋ b ⌋ = ⌊ N a b ⌋ \lfloor\frac{\lfloor \frac Na \rfloor}b\rfloor=\lfloor \frac N{ab}\rfloor ⌊b⌊aN⌋⌋=⌊abN⌋,所以讨论 4 4 4, 6 6 6 的情况会被 2 2 2 和 3 3 3 剪枝。而我们要计算的是 ⌊ N m ⌋ \lfloor \frac Nm\rfloor ⌊mN⌋,所以 m m m 可以被表示为 2 x 3 y 5 z 2^x3^y5^z 2x3y5z,而时间复杂度也只有 log 2 ( N ) log 3 ( N ) log 4 ( N ) \log_2(N)\log_3(N)\log_4(N) log2(N)log3(N)log4(N)。
AC Code(CPP):
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, a, x, y;
map<long long, double> m;
double f(int now) {
if (now == 0) return 0;
if (m[now]) return m[now];
double sum = y;
for (int i = 2; i <= 6; i++) {
sum += f(now / i) + y;
}
m[now] = min({sum / 5.0, f(now / a) + x});
return min({sum / 5.0, f(now / a) + x});
}
signed main() {
scanf("%lld%lld%lld%lld", &n, &a, &x, &y);
printf("%.16lf", f(n));
return 0;
}
AC Code(Python):
n, a, x, y = [int(i)for i in input().split()]
m = {}
def f(now):
if now == 0:
return 0
if now in m.keys():
return m[now]
cnt = y
for i in range(2, 7):
cnt += f(now // i) + y
m[now] = min(cnt / 5, f(now // a) + x)
return m[now]
print(f(n))
F
题目
可以通过递归函数计算。 f ( l , r , p ) f(l, r, p) f(l,r,p) 负责打印从 l l l 到 r r r 的字符。如果 p p p 等于 0 0 0,那么是正序打印,否则,是倒序打印。每遇到一个括号,就打印括号内的东西,打印顺序反过来。每一对对应的括号可以通过预处理得到。可以预先将每一个大小写预处理,就不用再打印时判断了。(如果括号层数为奇数就要翻转大小写。)或者在打印时判断。
于是可以在 O ∣ S ∣ O|S| O∣S∣ 的时间复杂度内求解问题。
AC Code(CPP):
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
char s[500100];
int n;
void init() {
string s1;
cin >> s1;
for (int i = 0; i < (int)s1.size(); i++) {
s[i + 1] = s1[i];
}
n = s1.size();
}
int idx[500100], idx1[500100];
void f(int l, int r, int p) {
if (p == 0) {
for (int i = l; i <= r; i++) {
if (s[i] == '(') {
f(i + 1, idx[i] - 1, 1);
i = idx[i];
}
else {
cout << s[i];
}
}
}
else {
for (int i = r; i >= l; i--) {
if (s[i] == ')') {
f(idx1[i] + 1, i - 1, 0);
i = idx1[i];
}
else {
cout << s[i];
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
init();
int cnt = 0;
stack<int> st;
for (int i = 1; i <= n; i++) {
if (s[i] == '(') {
cnt++;
st.push(i);
}
else if (s[i] == ')') {
cnt--;
idx[st.top()] = i;
idx1[i] = st.top();
st.pop();
}
else {
if (cnt % 2) {
if ('a' <= s[i] && s[i] <= 'z') {
s[i] -= 'a';
s[i] += 'A';
}
else {
s[i] -= 'A';
s[i] += 'a';
}
}
}
}
f(1, n, 0);
return 0;
}
AC Code(Python):
import sys
sys.setrecursionlimit(998244353)
s = input()
cnt = 0
idx = [0] * (len(s) + 1)
idx1 = [0] * (len(s) + 1)
stack = []
def f(l, r, p):
if p == 0:
temp = l
while temp <= r:
if s[temp] == '(':
f(temp + 1, idx[temp] - 1, 1)
temp = idx[temp] + 1
else:
print(s[temp], end='')
temp += 1
else:
temp = r
while temp >= l:
if s[temp] == ')':
f(idx1[temp] + 1, temp - 1, 0)
temp = idx1[temp] - 1
else:
if 'a' <= s[temp] <= 'z':
print(chr(ord(s[temp]) - 32), end='')
else:
print(chr(ord(s[temp]) + 32), end='')
temp -= 1
for i in range(len(s)):
if s[i] == '(':
cnt += 1
stack.append(i)
elif s[i] == ')':
cnt -= 1
idx[stack[len(stack) - 1]] = i
idx1[i] = stack[len(stack) - 1]
stack.pop(len(stack) - 1)
f(0, len(s) - 1, 0)