Codeforces 1084 - 题集

140 篇文章 0 订阅
80 篇文章 0 订阅

单个题目的链接

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://lucien.ink


题目链接

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 kabk 的路线去运行,而且电梯最多只会同时容纳一个人,现在有一个 n n n 层楼的楼房,第 i i i 层有 a i a_i ai 个人,这一层的每个人每天都要按照 i → 1 → i i \rightarrow 1 \rightarrow i i1i 的顺序使用电梯,问将电梯的 k k k 设为多少时,每天的运行距离最少。


思路

  注意到 n n n 只有 100 100 100,暴力枚举,复杂度 O ( n 2 ) O(n ^ 2) O(n2)


实现

https://pasteme.cn/2418

#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://lucien.ink


题目链接

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(nlog2(vmax))


实现

https://pasteme.cn/2419

#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://lucien.ink


题目链接

https://codeforces.com/contest/1084/problem/C


题意

  给你一串字符串,问你能选出多少个形如 aba 这样的子序列(任意两个 a 之间至少包含一个 b),a 也算,abaabbaab*ba 算同一种,aaba 是非法的,复杂度 O ( n ) O(n) O(n)


思路

  显然可以只保留 ab,然后把连续的 a 都提取出来,剩下的类似于统计 D A G DAG DAG 不同路径数那样统计就可以。


实现

https://pasteme.cn/2420

#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://lucien.ink


题目链接

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{fvcostu,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 (fv1costu,v1)+(fv2costu,v2)+wu

  答案在所有 f i f_i fi 与上述表达式中取最大值,复杂度为 O ( n ) O(n) O(n)


实现

https://pasteme.cn/2421

#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://lucien.ink


题目链接

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 个的字典树,最多能有多少个节点,模拟一下即可。


实现

https://pasteme.cn/2422

#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 多名。说实话,很难受,但也无法反驳什么,他们俩都比我要努力得多,唯一能做的就是加倍努力吧。

CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值