AtCoder Beginner Contest 349

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

A - Zero Sum Game (abc349 A)

题目大意

\(n\)个人游戏,每局有一人 \(+1\)分,有一人 \(-1\)分。

给定最后前 \(n-1\)个人的分数,问第 \(n\)个人的分数。

解题思路

零和游戏,所有人总分是 \(0\),因此最后一个人的分数就是前 \(n-1\)个人的分数和的相反数。

神奇的代码
n = input()
print(-sum([int(i) for i in input().split()]))


B - Commencement (abc349 B)

题目大意

对于一个字符串,如果对于所有 \(i \geq 1\),都有恰好 \(0\)\(2\) 个自负出现\(i\)次,则该串是好串。

给定一个字符串\(s\),问它是不是好串。

解题思路

\(|s|\)只有 \(100\),统计每个字符的出现次数,再枚举 \(i\)即可。

神奇的代码
#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;
    bool ok = true;
    map<char, int> cnt;
    for (auto c : s)
        cnt[c]++;
    auto check = [&](int c) {
        int cc = 0;
        for (auto& [k, v] : cnt) {
            cc += (v == c);
        }
        return cc == 0 || cc == 2;
    };
    for (int i = 1; i <= s.size(); ++i) {
        ok &= check(i);
    }
    cout << (ok ? "Yes" : "No") << endl;

    return 0;
}



C - Airport Code (abc349 C)

题目大意

给定一个字符串\(s\)和字符串 \(t\),问字符串 \(t\)能否从字符串 \(s\)得到。操作为:

  • \(s\)挑三个字母,不改变顺序变成 \(t\)
  • \(s\)挑两个字母,加上\(X\),不改变顺序变成 \(t\)

解题思路

就子序列匹配问题。就近匹配原则即可。

神奇的代码
#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, t;
    cin >> s >> t;
    if (t.back() == 'X')
        t.pop_back();
    auto pos = s.find_first_of(tolower(t[0]));
    for (int i = 1; i < t.size() && pos < s.size(); ++i) {
        pos = s.find_first_of(tolower(t[i]), pos + 1);
    }
    if (pos < s.size())
        cout << "Yes" << endl;
    else
        cout << "No" << endl;

    return 0;
}



D - Divide Interval (abc349 D)

题目大意

给定\([l,l+1,...,r-1,r)\)序列,拆分成最少的序列个数,使得每个序列形如 \([2^ij, 2^i(j+1))\)

给出拆分方案。

解题思路

拆分的序列个数最小,那肯定想让一个序列尽可能的长,而长的话,就是让\(2^i\)尽可能大。

因此就贪心地让\(2^i\)尽可能大,即 \(l=2^i * j\),这里的\(i\)是最大的 \(i\)(这意味着 \(j\)是奇数),并且 \(r \leq 2^i(j + 1)\),如果 \(r > 2^i(j + 1)\),那说明 \(2^i\)太大了,就变成 \(2^(i-1) 2j\)来试试。

神奇的代码
#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);
    LL l, r;
    cin >> l >> r;
    vector<array<LL, 2>> ans;
    while (l < r) {
        LL p2 = 1;
        LL bl = l;
        while (bl % 2 == 0 && l + p2 <= r) {
            bl >>= 1;
            p2 <<= 1;
        }
        while (l + p2 > r) {
            p2 >>= 1;
            bl <<= 1;
        }
        ans.push_back({l, l + p2});
        l += p2;
    }
    cout << ans.size() << '\n';
    for (auto& i : ans) {
        cout << i[0] << ' ' << i[1] << '\n';
    }

    return 0;
}



E - Weighted Tic-Tac-Toe (abc349 E)

题目大意

给定\(3 \times 3\)的网格,高桥和青木画 \(OX\)

每个格子有分数。

若存在同行同列或同对角线,则对应方赢,否则全部画满后,所画格子的分数和较大者赢。

问最优策略下,谁赢。

解题思路

朴素的博弈\(dp\),状态即为当前的棋盘样子,总状态数为 \(3^9=2e4\),直接搜索即可。

即设 \(dp[i]\)表示当前局面 \(i\)的当前操作者(称为先手)的必赢\((dp[i] = 1)\)或必输 \((dp[i] = 0)\)

转移,则枚举当前操作者的行为,即选择哪个格子,进入后继状态。

如果所有后继状态都是(先手)必赢,那么当前状态则是(先手)必输,即\(dp[i] = 0\)如果所有\(dp[j] = 1\)\(j\)是后继状态。

否则,如果有一个后继状态是先手必输,那么当前状态的先手就可以控制局面走向该状态,使得当前状态是必胜态,即\(dp[i] = 1\)如果存在一个\(dp[j] = 0\)

转移显而易见是\(O(9)\),总状态数只有 \(O(2e4)\),因此朴素搜索就可以通过了。

神奇的代码
#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);
    typedef array<array<LL, 3>, 3> tu;
    tu a;
    for (auto& x : a)
        for (auto& y : x)
            cin >> y;
    map<tu, int> dp;
    auto check_end = [&](tu& pos) -> int {
        LL p1 = 0, p2 = 0;
        int left = 0;
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                left += !pos[i][j];
                p1 += (pos[i][j] == 1) * a[i][j];
                p2 += (pos[i][j] == 2) * a[i][j];
            }
        }
        if (left == 0) {
            if (p1 > p2)
                return 0;
            else
                return 1;
        }

        for (int i = 0; i < 3; ++i) {
            if (pos[i][0] == pos[i][1] && pos[i][1] == pos[i][2] &&
                pos[i][0] != 0) {
                if (pos[i][0] == 1)
                    return 0;
                else
                    return 1;
            }
            if (pos[0][i] == pos[1][i] && pos[1][i] == pos[2][i] &&
                pos[0][i] != 0) {
                if (pos[0][i] == 1)
                    return 0;
                else
                    return 1;
            }
        }

        if (pos[0][0] == pos[1][1] && pos[1][1] == pos[2][2] &&
            pos[0][0] != 0) {
            if (pos[0][0] == 1)
                return 0;
            else
                return 1;
        }

        if (pos[0][2] == pos[1][1] && pos[1][1] == pos[2][0] &&
            pos[0][2] != 0) {
            if (pos[0][2] == 1)
                return 0;
            else
                return 1;
        }

        return -1;
    };

    auto dfs = [&](auto self, tu& pos, int role) -> bool {
        int status = check_end(pos);
        if (status != -1) {
            return dp[pos] = status == role;
        }
        if (dp.find(pos) != dp.end())
            return dp[pos];
        bool lose = false;
        for (auto& i : pos)
            for (auto& j : i) {
                if (j)
                    continue;
                j = (role + 1);
                lose |= (!self(self, pos, role ^ 1));
                j = 0;
            }
        return dp[pos] = lose ? 1 : 0;
    };

    tu ini{};
    bool win = dfs(dfs, ini, 0);
    cout << (win ? "Takahashi" : "Aoki") << endl;

    return 0;
}



F - Subsequence LCM (abc349 F)

题目大意

给定一个序列\(a\)\(m\),问子序列的数量,使得其\(lcm\)(最小公倍数)为 \(m\)

子序列之间的不同,当且仅当有元素在原位置不一样,即使它们的数字可能是一样的。

解题思路

我们可以依次考虑每个\(a\),选或不选,很显然是\(O(2^n)\)

我们不能维护每个数选择的状态,但是要维护怎样的状态呢?是怎样的中间状态能够导出它们的最小公倍数呢。

给定\(n\)个数,考虑怎么求它们的最小公倍数。

根据其定义,假设最小公倍数是\(m\),那就意味着 \(m\)是每个数的倍数。

从质因数的角度来思考倍数,那就是 \(m\)的每个质因子的幂 \(\geq\)每个数对应质因子的幂。

因此 \(m\)的每个质因子的幂就是这些数对应质因子幂的最大值。(最大公因数对应的其实就是幂的最小值)

从上述求最小公倍数的做法,可以引出维护的中间状态。

即设\(dp[i][j]\)表示考虑前 \(i\)个数,选择若干个后,每个质数幂的值的状态为 \(j\)的方案数。这样通过状态 \(j\)就可以知道最后的最小公倍数是不是 \(m\)。但很显然这一状态数是比较大的,稍加思考会发现我们不需要保留当前的幂值是多少,因为求最小公倍数时,我们是 取最大值,我们只想让它最后为\(m\)对应的值即可,因此这里的数量状态可以变成二元状态。

\(dp[i][j]\)表示考虑前 \(i\)个数,选择若干个后,其质数幂的最大值是否达到 \(m\)对应的质数幂的值的状态为 \(j\)(对于每个 \(m\)的质数,都有 未达到达到这一\(01\) 状态,因此\(j\)是一个二进制压缩的状态)的选择方案数。初始条件是 \(dp[0][0]=0\)

转移就考虑,选择当前数后,状态 \(j\)是否会变化。因此要事先预处理每个数选择后对这一状态的影响。即事先对\(m\)质因数分解,再预处理每个 \(a_i\)对转移的影响。

考虑时间复杂度, \(m\)\(10^{16}\),至多可能有15+个质数,总的复杂度是 \(O(2^15 10^5)\),粗略算也有 \(1e9\)了。当前的 \(dp\)还过不了。考虑进一步优化。

现在的问题已经变成,给定若干个 \(010101\)数,要求选若干个数,使得其与结果\(11111\)的方案数。上述的 \(dp\)方式复杂度是 \(1e9\),会超时。

超时的1e9
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int mo = 998244353;

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    LL m;
    cin >> n >> m;
    bool one = (m == 1);
    vector<pair<LL, int>> fac;
    int up = 1e8;
    for (int i = 2; i <= up; ++i) {
        if (m % i == 0) {
            fac.push_back({i, 0});
            while (m % i == 0) {
                m /= i;
                fac.back().second++;
            }
        }
    }
    if (m != 1) {
        fac.push_back({m, 1});
    }

    vector<int> a;
    for (int i = 0; i < n; ++i) {
        LL x;
        cin >> x;
        bool ok = true;
        int sign = 0;
        for (int j = 0; j < fac.size(); ++j) {
            auto& [p, v] = fac[j];
            int cnt = 0;
            while (x % p == 0) {
                x /= p;
                ++cnt;
            }
            ok &= (cnt <= v);
            if (cnt == v)
                sign |= (1 << j);
        }
        ok &= (x == 1);
        if (ok)
            a.push_back(sign);
    }

    vector<int> dp(1 << fac.size(), 0);
    dp[0] = 1;
    for (int x : a) {
        vector<int> dp2 = dp;
        for (int i = 0; i < (1 << fac.size()); ++i) {
            dp2[i | x] = (dp2[i | x] + dp[i]);
            if (dp2[i | x] >= mo)
                dp2[i | x] -= mo;
        }
        dp.swap(dp2);
    }
    int ans = dp.back();
    if (one) {
        ans = (ans - 1 + mo) % mo;
    }
    cout << ans << '\n';

    return 0;
}



欲知后事如何,且等作业写完后再写

G - Palindrome Construction (abc349 G)

题目大意

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

解题思路

<++>

神奇的代码



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

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

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

相关文章

  • AtCoder Beginner Contest 311

    给定一个字符串,问最短的一个前缀,包含 A B C 这三个字符。 注意到这个前缀的末尾字母一定是这三个字母中的一个,因此答案就是这三个字母出现位置最早的最大值。 神奇的代码 给定 (n) 个人的 (d) 天的空闲与否的情况,问最长的连续天,这 (n) 个人都空闲。 我们找

    2024年02月16日
    浏览(33)
  • 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)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包