AtCoder Beginner Contest 318

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

咕咕咕,总力战还没打,凹不过卷狗,躺了.jpg

A - Full Moon (abc318 A)

题目大意

给定\(n, m, p\),问有多少个 \(i\)满足 \(0 < m+pi \leq n\)

解题思路

减去初始的\(m\),剩下的就是看 \(p\)的倍数个数。

神奇的代码
#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, p;
    cin >> n >> m >> p;
    cout << (n - m) / p + (n >= m) << '\n';

    return 0;
}



B - Overlapping sheets (abc318 B)

题目大意

一个二维空间,有\(n\)个矩形覆盖。

问有多大的空间被覆盖了。重复覆盖算一次。

解题思路

空间大小只有\(100\),矩形只有 \(100\),对每个矩形直接 \(O(100^2)\)更新格子的覆盖情况,最后统计被覆盖的个数即可。

时间复杂度为 \(O(100^3)\)

神奇的代码
#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;
    array<array<int, 101>, 101> tu{};
    while (n--) {
        int a, b, c, d;
        cin >> a >> b >> c >> d;
        for (int i = a; i < b; ++i)
            fill(tu[i].begin() + c, tu[i].begin() + d, 1);
    }
    int ans = 0;
    for (auto& i : tu)
        ans += count(i.begin(), i.end(), 1);
    cout << ans << '\n';

    return 0;
}



C - Blue Spring (abc318 C)

题目大意

\(n\)天旅行,第 \(i\)天旅行花费 \(f_i\)元。

你可以购买一日游券,可以免去任意一天的旅游费用。

但是一日游券是捆绑售卖的,你需要花 \(p\)元买 \(d\)张 一日游券。这意味着你可以免去任意\(d\)天的旅游费用。你可以购买多次,最后一日游券可以剩余。

问旅游完 \(n\)天所需要的最小费用。

解题思路

假设我购买了\(x\)张一日游券,我肯定是免去旅游费用最高的那 \(x\)天的费用。

那就直接枚举买多少次一日游券,用于免去最高的旅游费用,然后其费用与剩余天数的费用相加求个最小值即可。

神奇的代码
#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, d, p;
    cin >> n >> d >> p;
    vector<int> f(n);
    for (auto& i : f)
        cin >> i;
    sort(f.begin(), f.end(), greater<int>());
    LL sum = accumulate(f.begin(), f.end(), 0ll);
    LL ans = sum;
    for (int i = 0; i < n; i += d) {
        for (int j = i; j < min(n, i + d); ++j)
            sum -= f[j];
        sum += p;
        ans = min(ans, sum);
    }
    cout << ans << '\n';

    return 0;
}



D - General Weighted Max Matching (abc318 D)

题目大意

给定一张无向图,边有边权。要求选择一些边,最大化边权和,使得每条边的顶点不重复。

解题思路

点数只有\(16\),考虑到每条边的顶点不能重复,那么当我们考虑某条边能不能选时,必须得知道目前已经选了哪些点,以决定这条边能不能选,因此设 \(dp[i]\)表示选择的点的情况是 \(i\)(二进制压缩的状态,0表示不选,1表示选) 的最大边权和。然后枚举一条边是否选择,转移到下一个状态即可。

神奇的代码
#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<vector<int>> edge(n);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n - i - 1; ++j) {
            int x;
            cin >> x;
            edge[i].push_back(x);
        }
    }
    vector<LL> dp((1 << n), 0);
    for (int i = 0; i < (1 << n); ++i) {
        for (int j = 0; j < n; ++j) {
            if ((~i >> j) & 1)
                continue;
            for (int k = j + 1; k < n; ++k) {
                if ((~i >> k) & 1)
                    continue;
                int w = edge[j][k - j - 1];
                dp[i] = max(dp[i], dp[i ^ (1 << j) ^ (1 << k)] + w);
            }
        }
    }
    cout << *max_element(dp.begin(), dp.end()) << '\n';

    return 0;
}



E - Sandwiches (abc318 E)

题目大意

给定一个\(n\)个数的数组 \(a\),问有多少个 \((i, j, k)\),满足

  • \(i < j < k\)
  • \(a_i = a_k\)
  • \(a_i \neq a_k\)

解题思路

由于数有\(10^5\),我们只能枚举其中一个。要么枚举 \(i\)要么枚举 \(j\)。分别考虑后发现枚举 \(j\)简单一点。

如果枚举下标\(i\)会发现比较难计算,对于当前的 \(a_i\),那么对于之后的每个 \(a_k\)都要考虑它们之间的其他数的个数,感觉复杂度会比较大。

考虑枚举下标\(j\),剩下的就是考虑 \(j\)左边和 \(j\)右边的相同数。这个\(j\)对答案的贡献就是 \(\sum_{x \neq a_j} l_{x} \times r_{x}\),其中 \(l_x\)表示 \(j\)左边 \(x\)(是值,不是下标)的个数, \(r_x\)就是右边的 \(x\)的个数。

考虑当 \(j\)移动时,只有 \(a_j\)\(a_{j + 1}\)这两个数的个数发生变化。因此我们事先维护 \(\sum\)的值,\(\sum - l_{a_j} \times r_{a_j}\)就是此时的 \(j\)对答案的贡献。转移时通过加减那两个数的贡献,就得到 \(j\)移动到下一个数时的 \(\sum\)的值。然后累计就是答案了。

神奇的代码
#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);
    vector<int> l(n, 0), r(n, 0);
    for (auto& i : a) {
        cin >> i;
        --i;
        r[i]++;
    }
    LL ans = 0;
    LL tmp = 0;
    for (int i = 0; i < n; ++i) {
        tmp -= 1ll * l[a[i]] * r[a[i]];
        r[a[i]]--;
        ans += tmp;
        l[a[i]]++;
        tmp += 1ll * l[a[i]] * r[a[i]];
    }
    cout << ans << '\n';

    return 0;
}



F - Octopus (abc318 F)

题目大意

给定\(n\)个宝藏的位置,以及\(n\)只长度为 \(l_i\)的手。你要求合法的位置的个数。

一个位置\(x\)合法,意味着你可以用\(n\)只手拿到 \(n\)个宝藏。一只手只能拿一个宝藏,且拿的范围为 \([x - l_i, x + l_i]\)

解题思路

考虑合法位置的情况,分三种,最左边,最右边,以及两个宝藏之间。

然后考虑最左边的种类,很显然,我们对手的长度进行从小到大排序,那就根据宝藏的坐标从小到大分配手。即对于第\(i\)个宝藏,分配的是长度为 \(l_i\)的手,那么能够及该宝藏的最左边的距离就是 \(x_i - l_i\),对该距离取个最大值,就是最左边的合法位置了。

然后考虑这个合法位置不断往右边移动,在到达第一个宝藏的位置前都是合法位置。

然后合法位置到了第一个宝藏和第二个宝藏之间。考虑在这区间移动,我们的手分配宝藏的方案是不变的,直到第二个宝藏距离当前位置比第一个位置更近时,分配方案才发生变化,需要重新计算。

注意到需要重新计算的情况,是宝藏距离当前位置的排名发生了变化,因此手分配宝藏的方案需要改变,重新计算当前位置的合法性。而排名发生变化的位置被称为关键位置

考虑这些关键位置在哪,既然是排名发生变化,那就是在某两个宝藏的中点,因此枚举左右两边宝藏,求个中点就能得到这些关键位置,注意到关键位置的数量只有\(O(n^2)\)个。

对于每个关键位置,重新计算下各个宝藏到该位置的距离,然后排个序分配手,在区间[当前关键位置,下一个关键位置-1],其手的分配方案都不变,此时对于左边的宝藏,会有一个最小值 \(L = x_i + l_i\),而对于右边的宝藏,会有个最大值 \(R = x_i - l_i\),这之间的[R, L]就是合法位置的区间。

因此对所有这样的关键位置累计\(L - R + 1\)的值就是答案。总的时间复杂度是 \(O(n^3 \log 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<LL> x(n), l(n);
    for (auto& i : x)
        cin >> i;
    for (auto& i : l)
        cin >> i;
    LL ans = 0;
    LL L = numeric_limits<LL>::min(), R = numeric_limits<LL>::max();
    for (int i = 0; i < n; ++i) {
        L = max(L, x[i] - l[i]);
        R = min(R, x[i] + l[n - i - 1]);
    }
    ans += max(0ll, x[0] - L);
    ans += max(0ll, R - x[n - 1] + 1);
    vector<int> id(n);
    vector<int> rk(n);
    iota(id.begin(), id.end(), 0);
    for (int i = 0; i < n - 1; ++i) {
        vector<LL> div{x[i]};
        for (int j = 0; j <= i; ++j) {
            for (int k = i + 1; k < n; ++k) {
                LL mid = (x[j] + x[k] + 1) / 2;
                if (mid > x[i] && mid < x[i + 1])
                    div.push_back(mid);
            }
        }
        sort(div.begin(), div.end());
        div.push_back(x[i + 1]);
        for (int k = 0; k < div.size() - 1; ++k) {
            LL mid = div[k];
            vector<LL> pos(n);
            for (int j = 0; j < n; ++j) {
                pos[j] = abs(x[j] - mid);
            }
            sort(id.begin(), id.end(), [&](int a, int b) {
                if (pos[a] != pos[b])
                    return pos[a] < pos[b];
                else
                    return a > b;
            });
            for (int j = 0; j < n; ++j)
                rk[id[j]] = j;
            L = div[k + 1] - 1, R = div[k];
            for (int j = 0; j <= i; ++j) {
                L = min(L, x[j] + l[rk[j]]);
            }
            for (int j = i + 1; j < n; ++j)
                R = max(R, x[j] - l[rk[j]]);
            ans += max(0ll, L - R + 1);
        }
    }
    cout << ans << '\n';

    return 0;
}




其实上面想复杂了(其实写也写复杂了,其实可以直接\(O(n^2)\),枚举宝藏求中点,然后遍历所有关键点,上述这么写是因为当时考虑的每两个宝藏之间的情况,所以就一段一段的),不过是一个很朴素的从特殊位置开始的思路。

还是考虑关键位置,这个关键位置可以是两个宝藏的中点,它们的距离排名发生了变化;也可以是某个宝藏使用某只手的极限位置(即\(x_i - l_j\)\(x_i + l_j\),以这些极限距离作为分割点\(div_i\),分别考虑每个分割点是否合法(同上面一样距离排序然后分配手),如果可以则代表 \([div_i, div_{i+1} - 1]\)都可以。可以参考jiangly的代码,但是正确性不太能理解,因为是以最优分配方式判断合法性,能保证这个区间都能取到最优方案吗(?

G - Typical Path Problem (abc318 G)

题目大意

给定一张简单无向图,问是否存在一个简单路径,从点\(a\)出发经过点 \(b\)到达点 \(c\)

解题思路

题目可以转化成是否存在一棵生成树,其根为\(b\),点 \(a\)和点 \(c\)\(lca\)是根,然后就一直在研究生成树,就不会了

神奇的代码



Ex - Count Strong Test Cases (abc318 Ex)

题目大意

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

解题思路

<++>

神奇的代码



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

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

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

相关文章

  • AtCoder Beginner Contest 336

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

    2024年01月20日
    浏览(43)
  • AtCoder Beginner Contest 301

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

    2024年02月04日
    浏览(56)
  • AtCoder Beginner Contest 321

    给定一个数,问从高位到低位,数字是不是递减的。 可以以字符串读入,然后依次判断即可。 神奇的代码 给定 (n-1) 个数字,问最后一个数字最少是多少,使得你的分数不小于 (x) 。 数字在 ([0,100]) 之间。分数是去掉最低最高后的和。 因为范围不大,直接枚举最后一个数

    2024年02月08日
    浏览(32)
  • AtCoder Beginner Contest 308

    这几天在收拾东西搬家,先附上代码,晚点补上题解 补完了 感觉这次FG都写不太明白 给定八个数,问是否满足以下要求: 不严格升序 每个数在 (100 sim 675) 之间 每个数都是 (25) 的倍数 依次对每个数判断是否符合这三个条件即可。 神奇的代码 高桥吃 (n) 份寿司,寿司分

    2024年02月11日
    浏览(31)
  • AtCoder Beginner Contest 332

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

    2024年02月04日
    浏览(41)
  • AtCoder Beginner Contest 345

    给定一个字符串,问是不是形如 ======...==== 的字符串。 根据长度构造出期望的字符串,再判断是否相等即可。 神奇的代码 给定 (a) ,输出 (lceil frac{a}{10} rceil) 上下取整的转换, (lceil frac{a}{10} rceil = lfloor frac{a + 9}{10} rfloor) 。用下取整即可。 Python 的 // 是下取证,

    2024年03月21日
    浏览(37)
  • AtCoder Beginner Contest 309

    感觉F写了个乱搞做法 给定一个 (3 times 3) 的网格,以及两个数字。 问这两个数字是否 水平相邻 。 求出两个数字的横纵坐标,看是否横坐标相同,纵坐标差一即可。 读题不仔细,开题就WA了。 神奇的代码 给定一个矩形。将最外围的数字顺时针旋转一格。 可以模拟一个指针

    2024年02月13日
    浏览(33)
  • AtCoder Beginner Contest 329

    劳累一天不该写题,启发式合并都写错了 给定一个字符串,将每个字符输出出来,中间留个空格。 遍历输出即可。 神奇的代码 给定一个数组,找出次大的数。 遍历一下,从非最大的数求一个最大值即可。 神奇的代码 给定一个字符串,问有多少个连续的子串,其由一个字母

    2024年02月05日
    浏览(76)
  • AtCoder Beginner Contest 339

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

    2024年02月19日
    浏览(32)
  • AtCoder Beginner Contest 340

    给定等差数列的 首项 、 末项 、 公差 。 输出这个等差数列。 从 首相 依次累加 公差 到 末项 即可。 神奇的代码 依次进行 (Q) 次操作,分两种。 1 x ,将 x 放到数组 (a) 的末尾。 2 k ,输出数组 (a) 的倒数第 (k) 项。 用 vector 模拟即可,操作 2 可快速寻址访问。 神奇的代

    2024年02月19日
    浏览(34)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包