单个题目的链接
Codeforces 1084A - The Fair Nut and Elevator
Codeforces 1084B - Kvass and the Fair Nut - 二分
Codeforces 1084C - The Fair Nut and String
Codeforces 1084D - The Fair Nut and the Best Path - 树形DP
Codeforces 1084E - The Fair Nut and Strings - 思维
Codeforces 1084A - The Fair Nut and Elevator
题解链接
题目链接
https://codeforces.com/contest/1084/problem/A
题意
有一个电梯,它初始时会停留在第 k k k 层楼,每当有一个人要从第 a a a 楼去第 b b b 楼,电梯就会始终按照 k → a → b → k k \rightarrow a \rightarrow b \rightarrow k k→a→b→k 的路线去运行,而且电梯最多只会同时容纳一个人,现在有一个 n n n 层楼的楼房,第 i i i 层有 a i a_i ai 个人,这一层的每个人每天都要按照 i → 1 → i i \rightarrow 1 \rightarrow i i→1→i 的顺序使用电梯,问将电梯的 k k k 设为多少时,每天的运行距离最少。
思路
注意到 n n n 只有 100 100 100,暴力枚举,复杂度 O ( n 2 ) O(n ^ 2) O(n2)。
实现
#include <bits/stdc++.h>
typedef long long ll;
ll a[1007], n, ans = 0x3f3f3f3f;
int main() {
scanf("%lld", &n);
for (ll i = 1; i <= n; i++) scanf("%lld", a + i);
for (ll i = 1; i <= n; i++) {
ll sum = 0;
for (ll j = 1; j <= n; j++) sum += a[j] * 2 * (abs(i - j) + abs(j - 1) + abs(1 - i));
ans = std::min(ans, sum);
}
printf("%lld\n", ans);
return 0;
}
Codeforces 1084B - Kvass and the Fair Nut - 二分
题解链接
题目链接
https://codeforces.com/contest/1084/problem/B
题意
你有 n n n 桶水,第 i i i 桶水里初始时有 v i v_i vi 升水,你需要从这 n n n 桶水中取出 s s s 升水,问是否能取 s s s 升水出来,如果可以的话让 n n n 桶水剩下水最少的那桶水尽量的多。
思路
直接暴力二分即可,复杂度 O ( n ⋅ l o g 2 ( v m a x ) ) O(n \cdot log_2(v_{max})) O(n⋅log2(vmax))。
实现
#include <bits/stdc++.h>
typedef long long ll;
const int maxn = 1007;
ll v[maxn], n, s, max, sum;
bool check(ll mid) {
ll ret = 0;
for (int i = 1; i <= n; i++) {
if (v[i] < mid) return false;
ret += v[i] - mid;
}
return ret >= s;
}
int main() {
std::cin >> n >> s;
for (int i = 1; i <= n; i++) std::cin >> v[i], sum += v[i], max = std::max(v[i], max);
if (sum < s) return 0 * puts("-1");
ll l = 0, r = max, ret = -1;
while (l <= r) {
ll mid = l + r >> 1;
if (check(mid)) ret = mid, l = mid + 1;
else r = mid - 1;
}
std::cout << ret << std::endl;
return 0;
}
Codeforces 1084C - The Fair Nut and String
题解链接
题目链接
https://codeforces.com/contest/1084/problem/C
题意
给你一串字符串,问你能选出多少个形如 aba
这样的子序列(任意两个 a
之间至少包含一个 b
),a
也算,aba
与 abba
与 ab*ba
算同一种,aaba
是非法的,复杂度
O
(
n
)
O(n)
O(n)。
思路
显然可以只保留 a
和 b
,然后把连续的 a
都提取出来,剩下的类似于统计
D
A
G
DAG
DAG 不同路径数那样统计就可以。
实现
#include <bits/stdc++.h>
const int maxn = int(1e5) + 7, mod = int(1e9) + 7;
char buf[maxn], str[maxn] = {'b'};
int len = 0, cnt[maxn], n = 0;
int main() {
scanf("%s", buf);
for (int i = 0; buf[i]; i++) if (buf[i] <= 'b') str[++len] = buf[i];
for (int i = 1; i <= len; i++) {
if (str[i] == 'a') {
if (str[i - 1] == 'b') cnt[++n] = 1;
else cnt[n]++;
}
}
long long ans = 0;
for (int i = 1; i <= n; i++) ans = (ans + (ans + 1) * cnt[i] % mod) % mod;
printf("%lld\n", ans);
return 0;
}
Codeforces 1084D - The Fair Nut and the Best Path - 树形DP
题解链接
题目链接
https://codeforces.com/contest/1084/problem/D
题意
给你一棵树,数上的每个点都有一个正权值,每条边都有一个负权值,让你在树中找一条简单路径,使得权值和最大。
思路
我们定义 f i f_i fi 为从以节点 i i i 为根的子树中出发,到达节点 i i i 时的最大权值和,转移方程为:
f u = m a x { f v − c o s t u , v + w u } f_u = max\{f_v - cost_{u, v} + w_u\} fu=max{fv−costu,v+wu}
这样处理的是竖着的路径,其中 v v v 是 u u u 的每一个儿子。穿过节点 i i i 的横向路径的最大值为:
( f v 1 − c o s t u , v 1 ) 最 大 值 + ( f v 2 − c o s t u , v 2 ) 次 大 值 + w u ({f_{v_1} - cost_{u, v_1}})_{最大值} + ({f_{v_2} - cost_{u, v_2}})_{次大值} + w_u (fv1−costu,v1)最大值+(fv2−costu,v2)次大值+wu
答案在所有 f i f_i fi 与上述表达式中取最大值,复杂度为 O ( n ) O(n) O(n)。
实现
#include <bits/stdc++.h>
const int maxn = int(3e5) + 7;
std::vector<std::pair<int, long long>> edge[maxn];
long long ans = 0, f[maxn];
int n, w[maxn];
void dfs(int u, int pre) {
f[u] = w[u];
long long max[2] = {0, 0};
for (auto cur : edge[u]) {
int v = cur.first;
long long cost = cur.second;
if (v != pre) {
dfs(v, u);
long long tmp = f[v] - cost;
if (tmp > max[0]) std::swap(max[0], tmp);
max[1] = std::max(max[1], tmp);
f[u] = std::max(f[u], f[v] - cost + w[u]);
}
}
ans = std::max({f[u], ans, max[0] + max[1] + w[u]});
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", w + i);
for (int i = 1, u, v, cost; i < n; i++) {
scanf("%d%d%d", &u, &v, &cost);
edge[u].emplace_back(v, cost);
edge[v].emplace_back(u, cost);
}
dfs(1, 1);
printf("%lld\n", ans);
return 0;
}
Codeforces 1084E - The Fair Nut and Strings - 思维
题解链接
题目链接
https://codeforces.com/contest/1084/problem/E
题意
给你一个区间
[
s
,
t
]
[s, t]
[s,t],其中
s
s
s 和
t
t
t 都是字符串,且长度为
n
n
n,只包含 ab
两种字符,问从这个区间内长度为
n
n
n 的字符串中挑出
k
k
k 个,最多可以有多少个不同的前缀。
思路
翻译一下,其实就是给你一个有左右边界且叶子数量必须小于等于 k k k 个的字典树,最多能有多少个节点,模拟一下即可。
实现
#include <bits/stdc++.h>
typedef long long ll;
const int maxn = int(5e5) + 7;
ll n, k, ans;
char a[maxn], b[maxn];
bool flag = true;
int main() {
scanf("%lld%lld%s%s", &n, &k, a + 1, b + 1);
ll sum = -1, cur = 1;
while (cur <= n && a[cur] == b[cur]) cur++, ans++;
for (ll i = cur; i <= n && flag; i++) {
sum = sum * 2 + 'b' - a[i] + b[i] - 'a';
if (sum + 2 >= k) ans += (n - i + 1) * k, flag = false;
else ans += sum + 2;
}
printf("%lld\n", ans);
return 0;
}
总结
前三题还算顺利,
D
D
D 题的时候松懈了,然后就忘了统计横向的路径,WA
了两发,很影响心态,
E
E
E 比赛的时候没想出来。
两个队友一个第 6 6 6 名,一个 34 34 34 名,只有我一个 200 200 200 多名。说实话,很难受,但也无法反驳什么,他们俩都比我要努力得多,唯一能做的就是加倍努力吧。