Dashboard - Codeforces Round 1026 (Div. 2) - Codeforces

A

将数组排序后,如果首尾相加为偶数,则直接输出0,否则枚举从前去掉的最少数字,和从后去掉的最少数字以达到首尾相加为偶数的最小值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void solve() {
int n;
cin >> n;
std::vector<int> a(n);
for (int &x : a) {
cin >> x;
}
std::sort(a.begin(), a.end());
if ((a[0] + a[n - 1]) % 2 == 0) {
cout << 0 << "\n";
return;
}
int ans = n - 1;
for (int i = 1; i < n - 1; i++) {
if ((a[i] + a[n - 1]) % 2 == 0) {
ans = std::min(ans, i);
break;
}
}
for (int i = n - 2; i > 0; i--) {
if ((a[0] + a[i]) % 2 == 0) {
ans = std::min(ans, n - i - 1);
break;
}
}
cout << ans << "\n";
}

B

如果某个前缀的)数量 $>$ (数量,那么它必然是不合法的括号序列

只需要判断在1~n-1位置的前缀上存在某个位置的(数量 $=$ )数量,只需要去掉这个前缀上的一个(,那么再去掉该位置之后的一个 )(可知n位置一定是一个)),整个括号序列就会变得不合法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void solve() {
string s;
cin >> s;
int n = s.size();
int bal = 0;
bool ok = false;
for(int i = 0; i < n - 1; i++){
if(s[i]=='(') {
bal++;
}
else{
bal--;
}
if(bal == 0){
ok = true;
break;
}
}
cout << (ok ? "YES\n" : "NO\n");
}

C

最初从 $(0,0)$ 开始飞行,考虑目前合法的飞行范围,假设前一个位置的合法飞行范围为 $(tl,tr)$,则

  • $d[i]=0$,则保持不变
  • $d[i]=1$,则 $(tl+1,tr+1)$
  • $d[i]=-1$,则 $(tl,tr+1)$

最后与当前位置的障碍物合并:设该位置障碍物为 $(l, r)$ 则取 $(tl,tr)\leftarrow(max(tl, l),min(tr,r))$ 为当前位置合法范围,如果有 $tl>tr$,无合法方案,返回-1

否则可从最后位置选取一个高度往前递推求出答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void solve() {
int n;
cin >> n;
std::vector<int> d(n);
for (int &x : d) {
cin >> x;
}
std::vector<PII> lr(n), hr(n);
for (auto &[l, r] : lr) {
cin >> l >> r;
}
for (int i = 0; i < n; i++) {
auto [tl, tr] = i > 0 ? hr[i - 1] : std::make_pair(0, 0);
if (d[i] == -1) {
tr++;
} else if (d[i] == 1) {
tl++;
tr++;
}
int curmn = std::max(lr[i].fi, tl), curmx = std::min(lr[i].se, tr);
if (curmn > curmx) {
cout << -1 << "\n";
return;
}
hr[i] = {curmn, curmx};
}
int h = hr[n - 1].se;
for (int i = n - 1; i >= 0; i--) {
if (d[i] == 1) {
h--;
} else if (d[i] == -1) {
if (h >= (i > 0 ? hr[i - 1].fi : 0) && h <= (i > 0 ? hr[i - 1].se : 0)) {
d[i] = 0;
} else {
d[i] = 1;
h--;
}
}
}
for (int i = 0; i < n; i++) {
cout << d[i] << " \n"[i == n - 1];
}
}

D

二分答案,用 $dp[i]$ 表示当前在节点 $i$ 拥有的电池数量,由于是个拓扑图,拓扑序从小到大,因此跑一遍拓扑序即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void solve() {
int n, m, mx = -1;
cin >> n >> m;
std::vector<std::vector<PII>> g(n);
std::vector<int> b(n);
for (int i = 0; i < n; i++) {
cin >> b[i];
}
for (int i = 0; i < m; i++) {
int s, t, w;
cin >> s >> t >> w;
s--;
t--;
mx = std::max(mx, w);
g[s].emplace_back(t, w);
}
auto check = [&](int x) -> bool {
std::vector<int> dp(n, -1);
dp[0] = std::min(x, b[0]);
for (int i = 0; i < n; i++) {
for (auto [v, w] : g[i]) {
if (w <= dp[i]) {
dp[v] = std::min(dp[i] + b[v], x);
}
}
}
return dp[n - 1] >= 0;
};
int l = 0, r = mx + 1;
while (l < r) {
int m = (l + r) / 2;
if (check(m)) {
r = m;
} else {
l = m + 1;
}
}
cout << (l == mx + 1 ? -1 : l) << "\n";
}

E

包含所有 $n$ 个点有且仅有一次

考虑欧拉路径:

  1. 所有 $v_i$ 相同的乐器和所有 $p_i$ 相同的节点之间可以相互到达
  2. 不能连续三次经过 $v_i$ 相同的节点或者 $p_i$ 相同的节点

可见选择为 $p_i\sim v_i\overset{v+i=v_j}\longrightarrow v_j\sim p_j\overset{p_j=p_k}\longrightarrow p_k \sim v_k$ 如果我们选择将相同的 $v$ 和相同的 $p$ 进行缩点,则可以发现所有点构成一个二分图,这样建图思路就清晰了,同时也能保证不在相同 $v$ 或 $p$ 的节点上连续跳转三次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
void solve() {
int n;
cin >> n;
std::vector<PII> a(n);
std::vector<int> v(n), p(n);
for (int i = 0; i < n; i++) {
cin >> v[i] >> p[i];
a[i] = {v[i], p[i]};
}
std::sort(v.begin(), v.end());
std::sort(p.begin(), p.end());
v.erase(std::unique(v.begin(), v.end()), v.end());
p.erase(std::unique(p.begin(), p.end()), p.end());
std::map<int, int> mv, mp;
int idx = 0;
for (int x : v) {
mv[x] = idx++;
}
for (int x : p) {
mp[x] = idx++;
}
std::vector<int> path, deg(idx);
std::vector<std::vector<PII>> g(idx);
std::vector<int> end(idx);
std::vector<bool> vis(n);
for (int i = 0; i < n; i++) {
int vi = mv[a[i].fi], pi = mp[a[i].se];
g[vi].emplace_back(pi, i);
g[pi].emplace_back(vi, i);
deg[vi]++;
deg[pi]++;
}
int odd = 0, s = -1;
for (int i = 0; i < idx; i++) {
if (deg[i] % 2 != 0) {
odd++;
s = i;
}
}
if (odd > 2) {
cout << "NO\n";
return;
}
if (s == -1) {
for (int i = 0; i < idx; i++) {
if (deg[i]) {
s = i;
break;
}
}
}
auto dfs = [&](auto && dfs, int u) -> void {
while (end[u] < g[u].size()) {
auto [v, id] = g[u][end[u]++];
if (!vis[id]) {
vis[id] = true;
dfs(dfs, v);
path.push_back(id);
}
}
};
dfs(dfs, s);
if (path.size() != n) {
cout << "NO\n";
return;
}
cout << "YES\n";
std::reverse(path.begin(), path.end());
for (int i = 0; i < n; i++) {
cout << path[i] + 1 << " \n"[i == n - 1];
}
}