AtCoder Beginner Contest 320

这篇具有很好参考价值的文章主要介绍了AtCoder Beginner Contest 320。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

A - Leyland Number (abc320 A)

题目大意

给定\(a,b\),输出 \(a^b + b^a\)

解题思路

因为数不超过\(10\),可以直接用 pow计算然后转成\(int\)。不会有精度损失。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int a, b;
    cin >> a >> b;
    cout << int(pow(a, b) + pow(b, a)) << '\n';

    return 0;
}



B - Longest Palindrome (abc320 B)

题目大意

给定一个字符串\(s\),求长度最长的回文串。

解题思路

因为长度只有\(100\),可以直接枚举子串,然后暴力判断是不是回文串(翻转后是否和自身相同),复杂度为 \(O(n^3)\)

数据范围大的话可以用Manachar算法\(O(n)\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    string s;
    cin >> s;
    int ans = 0;
    auto ok = [&](int l, int r) {
        string a = s.substr(l, r - l + 1);
        string b = a;
        reverse(b.begin(), b.end());
        return a == b;
    };
    for (int i = 0; i < s.size(); ++i)
        for (int j = i; j < s.size(); ++j) {
            if (ok(i, j))
                ans = max(ans, j - i + 1);
        }
    cout << ans << '\n';

    return 0;
}



C - Slot Strategy 2 (Easy) (abc320 C)

题目大意

给定\(n=3\)排灯,以及三个长度都为\(m\)的数字串,表示每一时刻,每盏灯依次循环地显示这些数字。

对于某一时刻,你最多只能按一个按钮,从而让一盏灯停下来。当然你可以什么都不操作。

问最终让三盏灯停下来时显示的数字都一样,需要的最短时间。

解题思路

考虑最朴素的做法,首先枚举最终情况下,显示的数字是什么,有\(10\)种情况。

然后考虑这三排灯按按钮的顺序,同样枚举这个顺序,有 \(3!\)种情况。

确定了数字和按按钮的顺序,剩下的就是模拟,只看一盏灯,然后出现这个数字时就按按钮,时间复杂度是 \(O(m)\)

最终的时间复杂度是\(O(n! \times 10nm)\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int inf = 1e9;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int m;
    cin >> m;
    array<string, 3> s;
    for (auto& i : s)
        cin >> i;
    int ans = 1e9;
    for (int i = 0; i <= 9; ++i) {
        array<int, 3> id{};
        iota(id.begin(), id.end(), 0);
        do {
            int cur = 0;
            int tot = 0;
            for (auto& j : id) {
                if (s[j].find(i + '0') == string::npos) {
                    tot = inf;
                    break;
                }
                while (s[j][cur] - '0' != i) {
                    cur = (cur + 1) % m;
                    ++tot;
                }
                cur = (cur + 1) % m;
                ++tot;
            }
            ans = min(ans, tot);

        } while (next_permutation(id.begin(), id.end()));
    }
    if (ans == inf)
        ans = 0;
    cout << ans - 1 << '\n';

    return 0;
}



D - Relative Position (abc320 D)

题目大意

二维平面,有\(n\)个人,其中第一个人在原点。

给定 \(m\)条信息 \((a,b,x,y)\),表示第 \(b\)个人的位置是 \(a_x + x, a_y + y\)。其中\(a_x,a_y\)表示第 \(a\)个人的位置。

问每个人的位置,或告知不确定。

解题思路

初始只有第一个人的位置确定。那么所有与第一个人相关的信息的其他人的位置都能确定。

有更多的人的位置确定了,那么与这些人相关的信息的更多的人的位置都可以确定。

这感觉上就像是一个关系网,从第一个人往外传播,遍及到能遍及到的人。

这就是一张图,我们根据这些信息建立一张无向图,然后从\(1\)号点开始 \(BFS\),确定中间每个人的位置。

未遍历到的点就是位置不确定的人。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<vector<array<int, 3>>> edge(n);
    for (int i = 0; i < m; ++i) {
        int a, b, x, y;
        cin >> a >> b >> x >> y;
        --a, --b;
        edge[a].push_back({b, x, y});
        edge[b].push_back({a, -x, -y});
    }
    queue<int> team;
    vector<int> ok(n);
    vector<array<LL, 2>> pos(n, {0, 0});
    ok[0] = 1;
    team.push(0);
    while (!team.empty()) {
        auto u = team.front();
        team.pop();
        for (auto& [v, x, y] : edge[u]) {
            if (!ok[v]) {
                pos[v] = {pos[u][0] + x, pos[u][1] + y};
                ok[v] = 1;
                team.push(v);
            }
        }
    }
    for (int i = 0; i < n; ++i) {
        if (ok[i])
            cout << pos[i][0] << ' ' << pos[i][1] << '\n';
        else
            cout << "undecidable" << '\n';
    }

    return 0;
}



E - Somen Nagashi (abc320 E)

题目大意

给定\(n\)个人,从左到右。

\(q\)个事件,每个事件形如 \((t, w, s)\),表示第 \(t\)时刻,当前最左边的人可以获得 \(w\)的分数,然后离开,直到 \(t+s\)时刻回来。

问最后每个人的分数。

解题思路

朴素的思想就是每一时刻迭代,但时刻高达\(10^9\)。注意到有很多时刻都是无事发生的,因此我们只用考虑有事件发生的,即有人离开和有人回来。

问题需要维护两个信息,一个是最左边的人的标号(就是当前最小的标号),以及何时会有人回来这一事件的发生。

注意到每次都是剔除标号最小的人,以及获取的标号最小的人,因此可以用一个优先队列维护当前还在的人的标号,是个小根堆。

至于维护有人何时会回来这一事件,注意到这个事件是有一个发生时间\(t+s\),而我们是按照时间流逝依次处理的,因此我们可以用另一个优先队列维护这些时间,这样就可以动态插入新的事件,通过时间从小到大排序,以及当前时刻就能判断事件是否发生。

两个优先队列模拟这个过程即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, m;
    cin >> n >> m;
    priority_queue<int> team;
    for (int i = 0; i < n; ++i)
        team.push(-i);
    priority_queue<array<int, 2>> event;
    vector<LL> ans(n);
    for (int i = 0; i < m; ++i) {
        int t, w, s;
        cin >> t >> w >> s;
        while (!event.empty() && -event.top()[0] <= t) {
            auto [_, u] = event.top();
            event.pop();
            team.push(-u);
        }

        if (!team.empty()) {
            int u = -team.top();
            team.pop();
            ans[u] += w;
            event.push({-(t + s), u});
        }
    }
    for (auto& i : ans)
        cout << i << '\n';

    return 0;
}



F - Fuel Round Trip (abc320 F)

题目大意

一维数轴,从\(0\)出发到 \(x_n\),再折返回来。

开车,油箱容量 \(h\),一距离消耗一单位汽油,有 \(n-1\)个加油站 ,对于第\(i\)个加油站,其位于 \(x_i\),可花\(p_i\)元加油 \(f_i\)单位汽油,但不能超过\(h\),超过部分则不要了,且花费价格不变 。

问出发再折返回来,消耗的最小钱数。注意一个加油站只能使用一次,这意味着如果过去用了,折返回来时不能再用了。

解题思路

本题有几个特别的地方,一个是油箱有上限,一个是加油是全加,另一个是有折返,加油站只能用一次。

如果没有加油站只能用一次的情况,注意到\(n\)\(h\)只有 \(300\)

考虑爆搜时的状态信息,我们可以保留 \(dp[i][j]\)表示到达第 \(i\)个加油站,此时还剩下 \(j\)升汽油的最小花费。然后枚举在第 \(i-1\) 个加油站时的汽油状态,考虑当前加油站是否加油,转移即可。

但是这个状态在返程时会有问题,因为对于第\(i\)个加油站能否加油,取决于去时是否加油,但这在状态里,这一信息没有保留下来。而记录每个加油站的使用状态,这是个指数级的状态,不行。

考虑棘手的地方,就是返程时到达了第\(i\)个加油站,此时无法做决策,因为我不知道来时,在第 \(i\)个加油站的状态。而如果我能一次就考虑第\(i\)个加油站,来回这两次的加油状态,就不会有上述问题。

思考如何一次就能考虑来回两次的状态 :即来时到达第\(i\)个加油站,此时有一个油箱量,回时到达第 \(i\)个加油站 ,也有一个油箱量。

\(dp[i][j][k]\)表示从\(i\)个加油站出发,此时油箱量为\(j\)(该站加油前),回来时油箱量为\(k\)(该站加油后)的最小费用。那么当前加油站一来一回的最优策略, 可以从下一个加油站一来一回的最优策略转移,枚举第\(i+1\)个加油站时的状态,以及考虑当前加油站来回时是否加油即可。

最后答案就是 \(dp[0][h][*]\)的最小值。初始条件为 \(dp[n][i][i] = 0\),其余为无穷大。

上述是 \(dp\)的整体方向,下述是一些实现细节,主要是由于油箱的上限和时间复杂度的优化。

代码里第一维是压缩掉了,实际维护的 \(dp[i][j][k][0/1]\)表示,在第 \(i\)个加油站,出发时 油箱量为\(j\),回来时为 \(k\),且回来时没加油/加了油的最小费用。注意这里的油箱量都是加油前的量,比如我选择出发时加了油,那么实际我转移时,容量起始为 \(min(j + f, h)\),如果我选择回来时加了油,那么实际转移时,容量起始为 \(min(k + f, h)\)

循环枚举的 \(j\)是从第 \(i\)个加油站出发时的油量(加油前), \(k\)是从第 \(i+1\)个加油站回来时的油量(加油后)。

这样在转移时直接通过路程来计算 去到\(i+1\)时 油量和回到\(i\)时的油量,不然如果枚举的都是第 \(i\)个加油站的状态的话,如果回来时加油了,且 \(k\)\(h\),要从\(i+1\)转移过来,这要考虑它的加油前的状态 ,分别有\(h, h - 1, h - 2, ..., h - f\),这又会有 \(O(h)\)的状态要枚举。

新增的 \(0/1\)状态也是为了能够转移而加上的。

于是代码看着就比较复杂了(但是大方向是上述的三维\(dp\),应该会有更优美的实现方式。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int inf = 1e9;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, h;
    cin >> n >> h;
    vector<int> pos(n + 1, 0);
    for (int i = 1; i <= n; ++i)
        cin >> pos[i];
    vector<array<int, 2>> info(n + 1, {0, 0});
    for (int i = 1; i < n; ++i) {
        cin >> info[i][0] >> info[i][1];
    }
    vector<vector<array<int, 2>>> dp(h + 1,
                                     vector<array<int, 2>>(h + 1, {inf, inf}));
    for (int i = 0; i <= h; ++i)
        dp[i][i][0] = 0;
    for (int i = n - 1; i >= 0; --i) {
        vector<vector<array<int, 2>>> dp2(
            h + 1, vector<array<int, 2>>(h + 1, {inf, inf}));
        int dis = pos[i + 1] - pos[i];
        auto [p, f] = info[i];
        auto [np, nf] = info[i + 1];
        for (int j = 0; j <= h; ++j) {
            for (int k = 0; k <= h; ++k) {

                if (j - dis >= 0 && k - dis >= 0)
                    dp2[j][k - dis][0] =
                        min(dp2[j][k - dis][0], dp[j - dis][k][0]);

                if (j - dis >= 0 && min(k + nf, h) - dis >= 0)
                    dp2[j][min(k + nf, h) - dis][0] =
                        min(dp2[j][min(k + nf, h) - dis][0], dp[j - dis][k][1]);

                if (min(j + f, h) - dis >= 0 && k - dis >= 0)
                    dp2[j][k - dis][0] = min(dp2[j][k - dis][0],
                                             dp[min(j + f, h) - dis][k][0] + p);

                if (min(j + f, h) - dis >= 0 && min(k + nf, h) - dis >= 0)
                    dp2[j][min(k + nf, h) - dis][0] =
                        min(dp2[j][min(k + nf, h) - dis][0],
                            dp[min(j + f, h) - dis][k][1] + p);

                if (j - dis >= 0 && k - dis >= 0)
                    dp2[j][k - dis][1] =
                        min(dp2[j][k - dis][1], dp[j - dis][k][0] + p);

                if (j - dis >= 0 && min(k + nf, h) - dis >= 0)
                    dp2[j][min(k + nf, h) - dis][1] = min(
                        dp2[j][min(k + nf, h) - dis][1], dp[j - dis][k][1] + p);
            }
        }
        dp.swap(dp2);
    }

    int ans = inf;
    for (int i = 0; i <= h; ++i)
        ans = min(ans, dp[h][i][0]);
    if (ans == inf)
        ans = -1;
    cout << ans << '\n';

    return 0;
}



一觉醒来想到了更优美的写法,同样是设\(dp[i][j][k]\)表示从\(i\)个加油站出发,此时油箱量为\(j\)(该站加油前),回来时油箱量为\(k\)(该站加油后)的最小费用。注意到去时的油箱要考虑加油前的,回是要考虑加油后的。至于原因,可以分别考虑加油前后的共四种情况,会发现某些情况可能会转移式不一样,某些可能转移的复杂度会更高。

循环枚举的 \(j\)是从第 \(i\)个加油站出发时的油量(加油前),\(k\)回到\(i\)个加油站的油量(加油前)。

这样转移时对于第\(i+1\)个加油站的情况就可以唯一确定了。转移式就更简洁一点,

注意枚举的\(k\)并不是 \(dp\)式的 \(k\)的意义,如果是加油后的话,再计算第 \(i+1\)个加油站的情况时,可能是 \(k-f+dis\),也可能是 \(k-1+dis\),因为加油是 \(min(k+f, h)\)。这会新增一个 \(O(n)\)的复杂度。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int inf = 1e9;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n, h;
    cin >> n >> h;
    vector<int> pos(n + 1, 0);
    for (int i = 1; i <= n; ++i)
        cin >> pos[i];
    vector<array<int, 2>> info(n + 1, {0, 0});
    for (int i = 1; i < n; ++i) {
        cin >> info[i][0] >> info[i][1];
    }
    vector<vector<int>> dp(h + 1, vector<int>(h + 1, inf));
    for (int i = 0; i <= h; ++i)
        dp[i][i] = 0;
    for (int i = n - 1; i >= 0; --i) {
        vector<vector<int>> dp2(h + 1, vector<int>(h + 1, inf));
        int dis = pos[i + 1] - pos[i];
        auto [p, f] = info[i];
        for (int j = 0; j <= h; ++j) {
            for (int k = 0; k <= h; ++k) {

                // 不加油
                if (j - dis >= 0 && k + dis <= h)
                    dp2[j][k] = min(dp2[j][k], dp[j - dis][k + dis]);

                // 去时加油
                if (min(j + f, h) - dis >= 0 && k + dis <= h)
                    dp2[j][k] =
                        min(dp2[j][k], dp[min(j + f, h) - dis][k + dis] + p);

                // 回时加油
                if (j - dis >= 0 && k + dis <= h)
                    dp2[j][min(k + f, h)] =
                        min(dp2[j][min(k + f, h)], dp[j - dis][k + dis] + p);
            }
        }
        dp.swap(dp2);
    }

    int ans = *min_element(dp[h].begin(), dp[h].end());
    if (ans == inf)
        ans = -1;
    cout << ans << '\n';

    return 0;
}



G - Slot Strategy 2 (Hard) (abc320 G)

题目大意

给定\(n\)排灯,以及长度都为\(m\)的数字串,表示每一时刻,每盏灯依次循环地显示这些数字。

对于某一时刻,你最多只能按一个按钮,从而让一盏灯停下来。当然你可以什么都不操作。

问最终让\(n\)盏灯停下来时显示的数字都一样,需要的最短时间。

解题思路

<++>文章来源地址https://www.toymoban.com/news/detail-709997.html

神奇的代码



到了这里,关于AtCoder Beginner Contest 320的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AtCoder Beginner Contest 341

    给定 (n) ,输出 (n) 个 (0) 和 (n+1) 个 (1) 交替的字符串。 (101010...) 循环输出即可。 神奇的代码 货币兑换。 (A) 国货币每 (x_a) 钱可兑换 (B) 国货币 (y_a) 钱。 (B) 国货币每 (x_b) 钱可兑换 (C) 国货币 (y_b) 钱。 ... 给定你拥有的每国货币钱数和兑换规则,依次兑换

    2024年02月19日
    浏览(25)
  • AtCoder Beginner Contest 328

    给定 (n) 个数字和一个数 (x) 。 问不大于 (x) 的数的和。 按找要求累计符合条件的数的和即可。 神奇的代码 给定一年的月数和一个月的天数。 问有多少对 ((i,j)) ,表示第 (i) 个月的第 (j) 日, (i,j) 的数位上每个数字都是一样的。 范围只有 (O(100^2)) ,枚举所有的

    2024年02月05日
    浏览(34)
  • AtCoder Beginner Contest 330

    给定 (n) 个学生的分数,以及及格分 (x) ,问多少人及格了。 依次判断即可。 神奇的代码 回答 (n) 个问题,每个问题给定 (a,l,r) ,问函数 (f(x) = |x - a|) 在 ([l,r]) 的最小值。 全定义域下,最小值显然在 (x=a) 取得。绝对值函数图像是 (V) 型。 现在限定在 ([l,r]) ,则

    2024年02月05日
    浏览(115)
  • AtCoder Beginner Contest 336

    给定一个数 (n) ,将 long 中的 o 重复 (n) 次后输出。 模拟即可。 神奇的代码 给定一个数 (n) ,问 (n) 的二进制表示下的末尾零的数量。 即找到最小的 (i) 使得 (n (1 i)) 不为零的位置。枚举即可。 或者直接用内置函数 __builtin_ctz 。(count tail zero? 神奇的代码 定义一个数

    2024年01月20日
    浏览(41)
  • AtCoder Beginner Contest 304

    依次给定每个人的姓名和年龄,排成一圈。从年龄最小的人依次输出姓名。 找到年龄最小的,依次输出就好了。 神奇的代码 给定一个数字,如果其超过三位数,则仅保留其最高三位,低位数字全部置为0。 读一个 string ,直接赋值即可。 神奇的代码 给定 (n) 个人的坐标,第

    2024年02月07日
    浏览(37)
  • AtCoder Beginner Contest 337

    给定 (n) 场比赛高桥和青木的得分。 问最后是总分,是高桥高还是青木高,还是打平了。 累计比较大小即可。 神奇的代码 给定一个字符串,问是否形如 AAAA..BBBB...CCCC 。 如果形如上述,那么通过 unique 函数去重后就是有序的,因此去重排序比较即可。 当然也可以朴素找出

    2024年01月21日
    浏览(32)
  • AtCoder Beginner Contest 310

    感觉F又双叒叕写复杂了 点杯咖啡,要 (p) 元,但可以用一个优惠券,使得咖啡只要 (q) 元,但你需要额外购买 (n) 个商品中(价格为 (a_i) )的一个。 问点杯咖啡的最小价格。 考虑直接买还是使用优惠券,使用优惠券的话就选 (n) 个商品中价格最小的。 两种情况取最小

    2024年02月16日
    浏览(25)
  • AtCoder Beginner Contest 325

    感觉错失了上分机会 给定姓和名,输出尊称,即姓+ san 。 按照题意模拟即可。 神奇的代码 给定 (n) 个地区的公司人数和对应的时区,规定上班时间为 (9:00-18:00) ,现召开一小时会议,上班期间的公司可以参加。问订个时间,能参与的人数的最大值。 枚举开会的时间,然后

    2024年02月08日
    浏览(30)
  • AtCoder Beginner Contest 301

    给定一个字符串表示高桥和青木每局的获胜情况。 如果高桥获胜局数多,或者两个胜局相等,但高桥率先取得那么多胜场,则高桥获胜,否则青木获胜。 问谁获胜。 按照题意,统计两者的获胜局数比较即可。 如果两者局数相等,可以看最后一局谁胜,青木胜则意味着高桥率

    2024年02月04日
    浏览(50)
  • AtCoder Beginner Contest 324

    在高铁上加训! 给定 (n) 个数,问是否都相等。 判断是不是全部数属于第一个数即可。或者直接拿 set 去重。 神奇的代码 给定一个数 (N) ,问是否能表示成 (2^x3^y) 。 指数范围不会超过 (64) ,花 (O(64^2)) 枚举判断即可。 神奇的代码 给定一个字符串 (t) 和若干个字符串

    2024年02月08日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包