AtCoder Beginner Contest 314

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

怎么好多陌生单词
审核怎么这么逆天,半小时还没审完

A - 3.14 (abc314 A)

题目大意

给定\(pi\)的值以及数 \(n\),要求保留 \(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 pi = "3."
                "14159265358979323846264338327950288419716939937510582097494459"
                "23078164062862089986280348253421170679";
    int x;
    cin >> x;
    cout << pi.substr(0, x + 2) << '\n';

    return 0;
}



B - Roulette (abc314 B)

题目大意

\(n\)个人玩轮盘赌游戏,简单说就是一个转盘有 \(37\)个数字以及一个箭头,箭头会等概率停在某个数字上。

给定每个人压了哪些数字,以及最终的结果数字 \(x\)

问压中数字 \(x\)的,且压数字的数量的最少的那些人是谁。

解题思路

按照题意,依次找每个人是否压\(x\),压了的话再看压的数字的数量是否是最少的,储存下来。

由于范围不大,判断是否压可以用 vector一个一个找,也可以用set

神奇的代码
#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;
    cin >> n;
    vector<set<int>> a(n);
    for (auto& i : a) {
        int c;
        cin >> c;
        while (c--) {
            int j;
            cin >> j;
            i.insert(j);
        }
    }
    int x;
    cin >> x;
    vector<int> ans;
    for (int i = 0; i < n; ++i) {
        if (a[i].find(x) != a[i].end()) {
            if (!ans.empty() && a[ans.front()].size() > a[i].size())
                ans.clear();
            if (ans.empty() || a[ans.front()].size() == a[i].size())
                ans.push_back(i);
        }
    }
    cout << ans.size() << '\n';
    for (auto& i : ans)
        cout << i + 1 << ' ';
    cout << '\n';

    return 0;
}



C - Rotate Colored Subsequence (abc314 C)

题目大意

给定一个字符串\(s\),每个字符都被涂了一个颜色。

要求将每种颜色的所有字符都往右循环移位一格,最右边的移动到第一位。

问移动后的字符串。

解题思路

每个颜色的字符都移动到同颜色的下一个字符。

因此记录下每个颜色的最后一次出现位置\(la\),当该颜色的新位置出现时,就知道该位置经过循环移位后的字母就是\(s[la]\)

再特别处理下该颜色第一次出现的位置,然后直接输出就可以了。

神奇的代码
#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;
    string s;
    cin >> n >> m >> s;
    vector<int> la(n, -1);
    vector<int> last(n);
    vector<int> col(n);
    for (int i = 0; i < n; ++i) {
        int c;
        cin >> c;
        --c;
        last[i] = la[c];
        col[i] = c;
        la[c] = i;
    }
    for (int i = 0; i < n; ++i) {
        if (last[i] == -1) {
            last[i] = la[col[i]];
        }
    }
    for (int i = 0; i < n; ++i)
        cout << s[last[i]];
    cout << '\n';

    return 0;
}



D - LOWER (abc314 D)

题目大意

给定一个字符串\(s\),进行三种操作:

  • \(s_x = c\)
  • 字符串\(s\)全部变成小写
  • 字符串\(s\)全部变成大写

经过\(q\)次操作后,输出 \(s\)

解题思路

由于\(n,q\)\(2e5\),暴力处理第二、三操作的复杂度是\(O(n)\),会超时。

操作二和操作三的影响会相互覆盖,因此我们可以只记录最后一次进行的是操作二还是操作三即可。

剩下的问题就是操作二、三是否会影响到操作一。

如何判断是否会影响到,实际上我们可以比较一下某个位置进行操作一的时间戳和进行操作二、三的时间戳,如果前者小则会影响到,否则就影响不到。

因此我们就记录下每个位置执行操作一的时间戳,并且 \(s_x = c\),并且也记录最后一次执行的操作二、三的时间戳。

最后输出的时候依次对每个字符判断一下操作二、三是否会影响到操作一修改的字符即可。

神奇的代码
#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;
    string s;
    int q;
    cin >> n >> s >> q;
    vector<int> time(n, 0);
    int all = -1, cur = -1;
    for (int i = 0; i < q; ++i) {
        int t, x;
        string c;
        cin >> t >> x >> c;
        if (t == 1) {
            --x;
            s[x] = c[0];
            time[x] = i;
        } else if (t == 2) {
            all = i;
            cur = 0; // low
        } else if (t == 3) {
            all = i;
            cur = 1; // up
        } else {
            assert(0);
        }
    }
    for (int i = 0; i < n; ++i) {
        if (time[i] < all) {
            if (cur == 0)
                cout << (char)tolower(s[i]);
            else if (cur == 1)
                cout << (char)toupper(s[i]);
            else
                assert(0);
        } else
            cout << s[i];
    }
    cout << '\n';

    return 0;
}



E - Roulettes (abc314 E)

题目大意

给定\(n\)个轮盘,每个轮盘有 \(p_i\)个数字,花费 \(c_i\)元去转它,等概率停在某个数字,你将获得对应数字的积分。

问达到 \(m\)积分的最小期望花费。

解题思路

期望题,一定记得从定义出发。

考虑每回合的操作,就是选择哪个轮盘去转,转了之后会有若干个后继状态,由于是概率,这里会有个期望。这里的后继状态会发现和当前考虑的状态是一样的。

问最小花费,那我们就是要选择最优的那个轮盘去转。

考虑\(dp[i]\)表示已经得到了积分\(i\),为了达到 \(m\)积分或以上还需要的最小花费。

然后就枚举转哪个轮盘,再枚举转这个轮盘到了哪个数字,对应了一个后继状态。根据期望定义,由于是等概率的\(dp[i]\)就是所有后继状态的平均值+当前转的花费。

举个例子,假设转的第\(j\)个轮盘有数字\(2,4,6,8\),则 \(dp[i] = c_j + \frac{1}{4} dp[i + 2] + \frac{1}{4} dp[i + 4] + \frac{1}{4} dp[i + 6] + \frac{1}{4} dp[i + 8]\)(这个式子就是期望的定义,某个状态的期望就是所有后继状态的概率加权平均

注意有\(0\)的存在,事先要把 \(0\)去掉,对应的花费也会增加。转移式移项一下就知道系数了。

举个例子,假设转的第\(j\)个轮盘有数字\(0,4,6,8\),则 \(dp[i] = c_j + \frac{1}{4} dp[i] + \frac{1}{4} dp[i + 4] + \frac{1}{4} dp[i + 6] + \frac{1}{4} dp[i + 8]\),移动一下项就是\(dp[i] = \frac{4}{3}c_j + \frac{1}{3} dp[i + 4] + \frac{1}{3} dp[i + 6] + \frac{1}{3} dp[i + 8]\)

复杂度就是\(O(mnp)\)

神奇的代码
#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<double> c(n);
    vector<vector<int>> a(n);
    for (int i = 0; i < n; ++i) {
        cin >> c[i];
        int x;
        cin >> x;
        int zero = 0;
        for (int j = 0; j < x; ++j) {
            int p;
            cin >> p;
            if (p)
                a[i].push_back(p);
            else
                ++zero;
        }
        c[i] = 1. * c[i] * x / (x - zero);
    }
    vector<double> dp(m, 0);
    for (int i = m - 1; i >= 0; --i) {
        double ans = 1e18;
        for (int j = 0; j < n; ++j) {
            double tmp = 0;
            for (auto& k : a[j]) {
                tmp += (i + k >= m ? 0 : dp[i + k]) + c[j];
            }
            tmp /= a[j].size();
            ans = min(ans, tmp);
        }
        dp[i] = ans;
    }
    cout << fixed << setprecision(10) << dp[0] << '\n';

    return 0;
}



F - A Certain Game (abc314 F)

题目大意

\(n\)个人,一开始每个人自成一队。

进行 \(n-1\)场比赛,每场比赛第 \(i,j\)人所在的队伍决斗,假设第 \(i\)所在队伍人数为 \(x\),第 \(j\)所在队伍人数为 \(y\),则前者获胜概率为 \(\frac{x}{x + y}\),后者为 \(\frac{y}{x + y}\)。比完后两队合并。

问最后每个人获胜的期望次数。

解题思路

假设第\(i\)个人所在队伍进行了 \(j\)场比赛,那它的期望获胜次数就是这 \(j\)场比赛的获胜概率的和。

但注意到每次获胜需要对这个队伍里的所有人都要加上一个概率,最坏的时间复杂度是 \(O(n^2)\)

优化的突破点肯定是想办法不让所有人都加,而是将它们视为一个整体(集合),让这个整体去加,最后再查每个人的概率和时再将这个效果加上,跟线段树的懒标记一个道理。

但注意到这个集合会变化的,比完赛后它们就会合并(线段树的就不会变化,它所维护的始终是那个区间的信息),这使得我们得保留这一时刻的集合状态

想到这里时会发现我们要维护的东西跟Kruskal重构树非常类似,或者说刚刚好就可以用它来维护,Kruskal重构树就是一个维护最小生成树构建时每一步的信息的树。

两个集合合并后会生成一个新的点,这个新的点就是这一时刻的集合状态,然后新点和旧点的连边边权就是获胜的概率,这样既懒标记的给整体加上了概率,同时也保留了当前的集合状态,使得该懒标记只生效于这个集合。

最终从根节点开始dfs,每个人的获胜概率就是根节点到该点的边权和。

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

constexpr LL mo = 998244353;

long long qpower(long long a, long long b) {
    long long qwq = 1;
    while (b) {
        if (b & 1)
            qwq = qwq * a % mo;
        a = a * a % mo;
        b >>= 1;
    }
    return qwq;
}

long long inv(long long x) { return qpower(x, mo - 2); }

class dsu {
  public:
    vector<int> p;
    vector<int> sz;
    int n;

    dsu(int _n) : n(_n) {
        p.resize(n);
        sz.resize(n);
        iota(p.begin(), p.end(), 0);
        fill(sz.begin(), sz.end(), 1);
    }

    inline int get(int x) { return (x == p[x] ? x : (p[x] = get(p[x]))); }

    inline bool unite(int x, int y) {
        x = get(x);
        y = get(y);
        if (x != y) {
            p[x] = y;
            sz[y] += sz[x];
            return true;
        }
        return false;
    }
};

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<vector<array<int, 2>>> edge(n + n - 1);
    int id = n - 1;
    dsu d(n + n - 1);
    fill(d.sz.begin() + n, d.sz.end(), 0);
    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        cin >> u >> v;
        --u, --v;
        int fu = d.get(u), fv = d.get(v);
        int szu = d.sz[fu], szv = d.sz[fv], invuv = inv(szu + szv);
        ++id;
        edge[id].push_back({fu, int(1ll * szu * invuv % mo)});
        edge[id].push_back({fv, int(1ll * szv * invuv % mo)});
        assert(d.unite(u, id));
        assert(d.unite(v, id));
    }
    vector<int> ans(n);
    function<void(int, int)> dfs = [&](int u, int p) {
        if (u < n) {
            ans[u] = p;
            return;
        }
        for (auto& [v, w] : edge[u]) {
            dfs(v, (p + w) % mo);
        }
    };
    dfs(id, 0);
    for (int i = 0; i < n; ++i)
        cout << ans[i] << " \n"[i == n - 1];

    return 0;
}



G - Amulets (abc314 G)

题目大意

\(n\)个怪兽和\(m\)种攻击类型,每种怪兽有一个攻击力和攻击类型。

依次挑战每个怪兽,直到打完所有怪兽或生命值非正。

依次对每个 \(k\),求,当你可以选择免疫 \(k\)种攻击类型的怪兽伤害时,最多能打死的怪兽数量。

对于每只怪兽,你首先生命值扣除怪兽攻击力的数值,如果你还活着,则你打死怪兽。

解题思路

考虑每个\(k\),注意到我们可以二分打死了\(x\)只怪兽,然后判断是否可行。

如何判可行,关键在于如何选择免疫的攻击类型。

考虑前\(x\)只怪兽每种攻击类型的和,我们肯定是免疫和最大的那\(k\)个攻击类型,然后看剩下的攻击力是否小于生命值。

这样的复杂度是 \(O(m^2 \log n)\)

感觉是整体二分,即没必要对每个问题单独二分求,而是可以将这\(m\)个问题通过一次二分解决,但其中的处理复杂度貌似不太对,得继续细想一下。

TBD.

神奇的代码



Ex - Disk and Segments (abc314 Ex)

题目大意

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

解题思路

<++>

神奇的代码



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

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

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

相关文章

  • AtCoder Beginner Contest 318

    咕咕咕,总力战还没打,凹不过卷狗,躺了.jpg 给定 (n, m, p) ,问有多少个 (i) 满足 (0 m+pi leq n) 减去初始的 (m) ,剩下的就是看 (p) 的倍数个数。 神奇的代码 一个二维空间,有 (n) 个矩形覆盖。 问有多大的空间被覆盖了。重复覆盖算一次。 空间大小只有 (100) ,矩形

    2024年02月10日
    浏览(30)
  • AtCoder Beginner Contest 328

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

    2024年02月05日
    浏览(27)
  • AtCoder Beginner Contest 323

    有的人边上课边打abc 给定一个 (01) 字符串,问偶数位(从 (1) 开始) 是否全为 (0) 。 遍历判断即可。 神奇的代码 给定 (n) 个人与其他所有人的胜负情况。问最后谁赢。 一个人获胜次数最多则赢,若次数相同,则编号小的赢。 统计每个人的获胜次数,排个序即可。 神奇

    2024年02月08日
    浏览(22)
  • AtCoder Beginner Contest 299

    给定一个包含 |*. 的字符串,其中 | 两个, * 一个,问 * 是否在两个 | 之间。 找到两个 | 的下标 (l, r) 以及 * 的下标 (mid) ,看看是否满足 (l mid r) 即可。 神奇的代码 给定 (n) 个人的卡片,颜色为 (c_i) ,数字为 (r_i) 。 如果其中有颜色为 (T) 的牌,则该颜色中数字最大

    2023年04月23日
    浏览(15)
  • AtCoder Beginner Contest 324

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

    2024年02月08日
    浏览(28)
  • AtCoder Beginner Contest 348

    给定 (n) ,输出 (ooxooxoox...) ,长度为 (n) 。 按题意模拟即可。 神奇的代码 给定 (n) 个点,对每个点,求出与其距离最远的点的下标。 (n) 只有 (100) ,对于每个点,花 (O(n)) 遍历每个点最大化距离,时间复杂度为 (O(n^2)) 。 神奇的代码 (n) 个豌豆,每个豌豆有美味值

    2024年04月08日
    浏览(33)
  • AtCoder Beginner Contest 325

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

    2024年02月08日
    浏览(23)
  • AtCoder Beginner Contest 330

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

    2024年02月05日
    浏览(108)
  • AtCoder Beginner Contest 332

    坐地铁时口糊了6题,回来写时结果爆 long long , 0 没有逆元,卡了好久 线上购物,买了 (n) 种物品,分别给出它们的单价和数量。 若总价少于 (s) 元,则需要支付 (k) 元邮费,否则包邮。 问总价多少。 求个和判断下是否加邮费即可。 神奇的代码 一个容量为 (G) 的玻璃杯

    2024年02月04日
    浏览(31)
  • 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日
    浏览(19)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包