AtCoder Beginner Contest 336

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

A - Long Loong (abc336 A)

题目大意

给定一个数\(n\),将 long中的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);
    int n;
    cin >> n;
    cout << "L" << string(n, 'o') << "ng" << '\n';

    return 0;
}



B - CTZ (abc336 B)

题目大意

给定一个数\(n\),问 \(n\)的二进制表示下的末尾零的数量。

解题思路

即找到最小的\(i\)使得 \(n & (1 << i)\) 不为零的位置。枚举即可。

或者直接用内置函数 __builtin_ctz。(count tail zero?

神奇的代码
#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 x;
    cin >> x;
    cout << __builtin_ctz(x) << '\n';

    return 0;
}



C - Even Digits (abc336 C)

题目大意

定义一个数种类,每个数位都是偶数。

给定\(n\),问第 \(n\)大的数。

解题思路

每一位的取值只有0,2,4,6,8五种,其实就是一个5进制的转换,再将每个数位乘以\(2\)即可。

神奇的代码
#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 n;
    cin >> n;
    --n;
    string ans;
    while (n) {
        ans += (n % 5) * 2 + '0';
        n /= 5;
    }
    reverse(ans.begin(), ans.end());
    if (ans.empty())
        ans = "0";
    cout << ans << '\n';

    return 0;
}



D - Pyramid (abc336 D)

题目大意

给定一个数组\(a\),求经过以下操作可以得到的最长的金字塔序列。

金字塔序列形如\(1,2,3,4,5,...,k-1,k,k-1,...,5,4,3,2,1\)

可进行的操作为:

  • 将某数减1
  • 将开头或末尾的数移除

解题思路

考虑这个金字塔序列的特点,如果选择中间最高点的\(k\)的位置,以及 \(k\)的值,那剩下的就是看从该位置左右延伸,是否都不小于对应的值。但这复杂度是 \(O(n^3)\),不大行。

注意到金字塔序列是一个对称的,可以考虑 \(1\)的位置,然后看看能往左和往右延伸到多远。那最后再枚举左边的 \(1\)的位置和右边 \(1\)的位置, 看看它们往右和往左延伸的距离能不能覆盖这两个\(1\)之间的值。

考虑如何求从 \(a_i\)往右能延伸多远,假设能延伸到\(a_j\),就要要求这期间的 \(a_k\)都满足 \(a_k \leq k - i + 1\)。朴素求法是 \(O(n^2)\)。但注意到一个性质,比如 \(a_i\)能延伸到 \(a_j\),那 \(a_{i+1}\)至少也会延伸到 \(a_{j}\),因为原本在\(a_i\)为起点时,\(k \in [i,j]\)需要满足 \(a_k \leq k - i + 1\), 而以\(a_{i+1}\)为起点时需要满足的是 \(a_k \leq k - i\) ,条件更松了,之前满足的也肯定满足,因此求\(a_{i+1}\)时直接从上次的 \(a_j\)继续往右延伸即可,而不必重新从\(a_{i+1}\)开始求 。即双指针的求法,复杂度是\(O(n)\)

往左和往右的求法是一样的,当求出从 \(a_i\)往左和往右延伸的最远距离 \(l_i\)\(r_i\)后,如果枚举两个 \(1\)的位置判断,那还是 \(O(n^2)\)不行。但容易发现这个也可以用双指针的方法枚举两个 \(1\)的位置。因此时间复杂度就是 \(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);
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto& x : a)
        cin >> x;
    auto solve = [&](vector<int>& a) {
        vector<int> pos(n);
        int l = 0;
        for (int i = 0; i < n; ++i) {
            while (l < i && a[i] <= i - l) {
                pos[l] = i - l;
                ++l;
            }
        }
        while (l < n) {
            pos[l] = n - l;
            ++l;
        }
        return pos;
    };
    auto r = solve(a);
    ranges::reverse(a);
    auto l = solve(a);
    int ans = 0;
    ranges::reverse(l);
    int R = 0;
    for (int L = 0; L < n; ++L) {
        while (R < n && r[L] + l[R] >= R - L + 1) {
            if ((R - L + 1) & 1)
                ans = max(ans, (R - L + 1) / 2 + 1);
            ++R;
        }
    }
    cout << ans << '\n';

    return 0;
}



E - Digit Sum Divisible (abc336 E)

题目大意

给定\(n\),问 \(1 \sim n\)中,能被自己的数位和整除的数的个数。

解题思路

\(n\)\(10^{14}\),显然不能枚举求。

对能整除的数的计数,可以从高位到低位一位一位地考虑,只要知道高位对除数的取余结果,就可以决定低位的取值,进而知道取余的结果变为多少,最后看是否为 \(0\)来判断是不是整除。即一个经典的数位\(dp\)的形式。

但棘手的是 除数不是固定的,它是由一个数的数位和决定,在一般的数位\(dp\)过程中,其数位和会变化,即除数会变化,这就导致余数不能求得,进而最后的能否整除就难以判断。

但注意到数位和的范围不大,它只有\([1,9\times 14]\),可以考虑枚举数位和\(sum\), 那剩下的就是求:数位和是\(sum\),且对\(sum\) 取余结果为\(0\)的值的数的个数。

这两个都是常规条件,数位\(dp\)即可解决。

时间复杂度为\(O((10\log n)^4)\)

数位\(dp\)就考虑递归的形式,从高位到低位考虑每一位的取值,记录中间状态有:当前考虑的位数,取值是否受\(n\)的限制,高位的取余结果,已经填的高位的数位和(下述代码里是低位需要的数位和,反了过来)。然后记忆化一下。记忆化的数组\(dp[pos][mod][sum]\)可以理解成:已经填了\(pos\)的高位,其取余结果为 \(mod\),数位和为 \(sum\)时,低位的全部填法中,满足题目要求(取余为\(0\)和数位和)的填法数量。

有时候 \(dp\)数组会加一维 \(limit\),这个可有可无, 没有的话就这部分会重复求解,但因为数量不多,所以不会太费时间。有的话就可能会更快,但会费点空间。

神奇的代码
#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 n;
    cin >> n;
    vector<int> s;
    while (n) {
        s.push_back(n % 10);
        n /= 10;
    }
    ranges::reverse(s);
    LL ans = 0;
    int maxsum = 9 * min(14ul, s.size());
    vector<vector<vector<LL>>> dp(
        s.size(), vector<vector<LL>>(maxsum + 1, vector<LL>(maxsum + 1, -1)));
    auto solve = [&](auto self, int pos, int limit, int mod, int sum,
                     int div) -> LL {
        if (sum < 0)
            return 0ll;
        if (pos == s.size())
            return LL(mod == 0 && sum == 0);
        if (!limit && dp[pos][mod][sum] != -1)
            return dp[pos][mod][sum];
        int up = (limit ? s[pos] : 9);
        LL ret = 0;
        for (int i = 0; i <= up; ++i) {
            ret += self(self, pos + 1, limit && i == up, (mod * 10 + i) % div,
                        sum - i, div);
        }
        if (!limit)
            dp[pos][mod][sum] = ret;
        return ret;
    };
    for (int i = 1; i <= maxsum; ++i) {
        for (auto& i : dp)
            for (auto& j : i)
                ranges::fill(j, -1);
        auto ret = solve(solve, 0, 1, 0, i, i);
        ans += ret;
    }
    cout << ans << '\n';

    return 0;
}



F - Rotation Puzzle (abc336 F)

题目大意

给定一个\(h \times w\)的矩阵,\([1,h\times w]\)的恰好填了一次。

问能否经过不超过 \(20\)次操作,使得矩阵变成一个正常的矩阵,即\((i,j)\)上的数是 \(((i-1) \times w + j)\)。若可,输出最小的操作次数。

操作为:选定一个 \((h-1) \times (w-1)\) 的子矩阵,旋转\(180\)度。

解题思路

注意到只有\(4\)个子矩阵,那每次操作其实只有 \(4\)种选择,直接\(BFS\)的复杂度是 \(O(4^20)\),即 \(O(2^40)\)

注意到这是个非常特别的复杂度,它的一半即 \(O(2^20)\)是可做的。

注意到起始局面和终点局面都是已知的,且操作都是可逆的。因此可以从起点和终点分别$BFS$10步, 然后看搜得的局面是否有交集,取步数和最小值即可。

神奇的代码
#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 h, w;
    cin >> h >> w;
    vector<vector<int>> a(h, vector<int>(w));
    for (auto& i : a)
        for (auto& j : i)
            cin >> j;
    vector<vector<int>> b(h, vector<int>(w));
    for (int i = 0, cnt = 1; i < h; ++i)
        for (int j = 0; j < w; ++j) {
            b[i][j] = cnt;
            cnt++;
        }
    auto tr = [&](vector<vector<int>>& st, int x, int y) {
        auto ret = st;
        for (int i = x; i < x + h - 1; ++i)
            for (int j = y; j < y + w - 1; ++j) {
                ret[i][j] = st[h - 2 - (i - x) + x][w - 2 - (j - y) + y];
            }
        return ret;
    };
    auto BFS = [&](vector<vector<int>>& st, int up) {
        map<vector<vector<int>>, int> dis;
        queue<vector<vector<int>>> team;
        team.push(st);
        dis[st] = 0;
        while (!team.empty()) {
            auto u = team.front();
            team.pop();
            if (dis[u] == up)
                break;
            for (int i = 0; i <= 1; ++i)
                for (int j = 0; j <= 1; ++j) {
                    auto v = tr(u, i, j);
                    if (dis.count(v))
                        continue;
                    dis[v] = dis[u] + 1;
                    team.push(v);
                }
        }
        return dis;
    };
    auto sol1 = BFS(a, 10);
    auto sol2 = BFS(b, 10);
    int ans = inf;
    for (auto& [tu, dis] : sol1) {
        if (sol2.count(tu)) {
            ans = min(ans, dis + sol2[tu]);
        }
    }
    if (ans == inf)
        ans = -1;
    cout << ans << '\n';

    return 0;
}



G - 16 Integers (abc336 G)

题目大意

给定\(16\)个数 \(x_{i,j,k,l}\)

\(n\)为这 \(16\)个数的和。

问一个长度为 \(n+3\)\(01\)数组\(a\)的数量,满足以下条件:

  • 对于每一个数 \(x_{i,j,k,l}\),数组\(a\)中恰好有 \(x_{i,j,k,l}\)个下标\(s\)满足:\(a_s = i, a_{s+1} = j, a_{s + 2}=k, a_{s+3}=l\)

解题思路

感觉是个神仙题,以后再来探索吧文章来源地址https://www.toymoban.com/news/detail-808806.html

神奇的代码



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

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

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

相关文章

  • Atcoder beginner contest 336 -- E -- Digit Sum Divisible --- 题解(数位dp)

    目录   E -- Digit Sum Divisibl 题目大意: 思路解析: 代码实现: 给你一个整数n,让你找出小于等于n的数中一共有多少个好整数,并输出好整数的个数。对好整数的个数定义为如果一个数能被他的数位之和整除,则称这个数为好整数。例如 12 能被 3 整除。 n=10^14。 看到数位之和

    2024年01月16日
    浏览(29)
  • AtCoder Beginner Contest 292 (A - E) 记录第一场ABC

    本来晚上在打Acwing周赛,最后一题Trie想不出来咋写,看群里有人说ABC要开始了,想着没打过ABC就去报了一下,感觉难度大概是cf的Div3到Div4之间吧,总共写了五个题,E题想复杂了快结束才交过。总的来说手速很重要。 题意:给一个字符串,要求把小写字母改成大写。 分析:

    2023年04月19日
    浏览(21)
  • AtCoder abc336 A~D题解

    题目翻译: 对于正整数 X X X 级别的龙串, X X X 是长度为 ( X + 3 ) (X+3) ( X + 3 ) 的字符串,由按此顺序排列的 o 、 n 和 g 的一次 L 、 X X X 次出现形成。 你得到一个正整数 N N N 。打印 N N N 级的龙串。 分析 按题目要求做即可……,输出一个 L ,循环 X X X 次输出 o ,再输出 ng 。

    2024年01月15日
    浏览(30)
  • AtCoder Beginner Contest 339

    给一个网址,问它的后缀是多少。 找到最后的\\\'.\\\'的位置,然后输出后面的字符串即可。 python 可以一行。 神奇的代码 二维网格,上下左右相连,左上原点。初始全部为白色,位于原点,面朝上。 进行 (n) 次操作,每次操作,将当前格子颜色黑白反转,然后 如果原来是白色

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

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包