AtCoder Beginner Contest 309

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

感觉F写了个乱搞做法

A - Nine (abc309 A)

题目大意

给定一个\(3 \times 3\)的网格,以及两个数字。

问这两个数字是否水平相邻

解题思路

求出两个数字的横纵坐标,看是否横坐标相同,纵坐标差一即可。

读题不仔细,开题就WA了。

神奇的代码
#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;
    -- a, -- b;
    if (abs(a % 3 - b % 3) == 1 && abs(a / 3 - b / 3) == 0)
        cout << "Yes" << '\n';
    else 
        cout << "No" << '\n';

    return 0;
}



B - Rotate (abc309 B)

题目大意

给定一个矩形。将最外围的数字顺时针旋转一格。

解题思路

可以模拟一个指针跑,也可以分别考虑四个边界的转移公式。

神奇的代码
#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<string> a(n);
    for(auto &i : a)
        cin >> i;
    auto ans = a;
    for(int i = 0; i < n; ++ i){
        ans[0][i] = i ? a[0][i - 1]: a[1][0];
        ans[i][0] = i == n - 1 ? a[n - 1][1] : a[i + 1][0];
        ans[n - 1][i] = i == n - 1 ? a[n - 2][n - 1] : a[n - 1][i + 1];
        ans[i][n - 1] = i ? a[i - 1][n - 1] : a[0][n - 2];
    }
    for(auto &i : ans){
            cout << i;
        cout << '\n';
    }

    return 0;
}



C - Medicine (abc309 C)

题目大意

给定\(n\)种药,第 \(i\)种药吃 \(a_i\)天,每天 \(b_i\)粒。

问最早到第几天,吃的药的粒数 \(\leq K\)

解题思路

我们可以从小到大枚举天数,直到某天吃的药的粒数\(\leq K\),但天数高达\(O(10^9)\),直接枚举会超时。

但是我们会发现,每天的吃的药的粒数会越来越少,因此每天吃的药的粒数和天数之间有单调性

于是我们可以二分天数,判断这一天吃的药的粒数是否\(\leq K\),进而排除一半的答案,搜索另一片。

神奇的代码
#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, k;
    cin >> n >> k;
    vector<array<int, 2>> a(n);
    for(auto &i : a)
        cin >> i[0] >> i[1];
    int l = 0, r = 1e9 + 7;
    auto check = [&](int x){
        LL tot = 0;
        for(auto &i : a){
            tot += i[1] * (i[0] >= x);
        }
        return tot > k;
    };
    while(l + 1 < r){
        int mid = (l + r) >> 1;
        if (check(mid))
            l = mid;
        else 
            r = mid;
    }
    cout << r << '\n';

    return 0;
}



D - Add One Edge (abc309 D)

题目大意

给定两个连通块,现在从这两个连通块中各选一个点,连一条边,问\(1\)号点和 \(n_1+n_2\)号点的最小距离的最大值。

解题思路

很显然,从\(1\)号点到 \(n_1+n_2\)号点,其路径是 \(1 \to l \to r \to n_1 + n_2\)。其中\(l\)号点在第一个连通块,\(r\)号点在第二个联通块,是我们选择连边的两个点,其中 \(l \to r\)距离是 \(1\),最大化总距离,那就是最大化 \(1 \to l\)\(r \to n_1 + n_2\)

那就分别从\(1\)号点和 \(n_1 + n_2\)号点分别 \(BFS\),找到最远的距离的点,连边即是答案。

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

const int inf = 1e9 + 7;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n1, n2, m;
    cin >> n1 >> n2 >> m;
    vector<array<int, 2>> edges(m);
    vector<vector<int>> e(n1 + n2);
    for(int i = 0; i < m; ++ i){
        int u, v;
        cin >> u >> v;
        -- u, -- v;
        edges[i] = {u, v};
        e[u].push_back(i);
        e[v].push_back(i);
    }
    vector<int> dis(n1 + n2, inf);
    auto BFS = [&](int st){
        dis[st] = 0;
        queue<int> team;
        team.push(st);
        while(!team.empty()){
            int u = team.front();
            team.pop();
            for(auto &i : e[u]){
                int v = u ^ edges[i][0] ^ edges[i][1];
                if (dis[v] > dis[u] + 1){
                    dis[v] = dis[u] + 1;
                    team.push(v);
                }
            }
        }
    };
    BFS(0);
    BFS(n1 + n2 - 1);
    cout << *max_element(dis.begin(), dis.begin() + n1) + *max_element(dis.begin() + n1, dis.end()) + 1 << '\n';

    return 0;
}



E - Family and Insurance (abc309 E)

题目大意

给定一棵(?或者若干棵)树,以及\(m\)次操作。

每次操作将以\(x_i\)号点的子树,到根距离不超过\(y_i\) 的点都加一。

问有多少个点至少加了一。

解题思路

如果将问题简化成一条链,那很显然就是个区间加问题,差分一下,每次操作就变成一个点加一,一个点减一,最后再求一遍前缀和还原数组就解决了。

但本题是树,与链的区别是,每次操作是一个点加一,很多个点减一(并且减一的点不能很快找到)。

但考虑对这棵树\(DFS\),在某一刻,我们维护的信息其实就是当前点到根的这条链,于是问题貌似就解决了?

\(DFS\)时,实时维护上述的差分数组该差分数组的和(即在搜索到该点时才考虑对该点操作的影响,影响都是对后续的影响),回溯的时候消除对该点操作影响到的差分数组即可。

神奇的代码
#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<int>> edge(n);
    for(int i = 1; i < n; ++ i){
        int v;
        cin >> v;
        -- v;
        edge[v].push_back(i);
    }
    int ans = 0;
    vector<int> visit(n, false);
    vector<int> diff(n + 1, 0);
    int sum = 0;
    vector<int> ins(n, 0);
    for(int i = 0; i < m; ++ i){
        int x, y;
        cin >> x >> y;
        -- x;
        ins[x] = max(ins[x], y);
    }
    function<void(int, int)> dfs = [&](int u, int deep){
        visit[u] = 1;
        if (ins[u]){
            ++ sum;
            diff[min(n, deep + ins[u] + 1)] -= 1;
        }
        sum += diff[deep];
        if (sum)
            ++ ans;
        for(auto &v : edge[u]){
            dfs(v, deep + 1);
        }
        sum -= diff[deep];
        if (ins[u]){
            -- sum;
            diff[min(n, deep + ins[u] + 1)] += 1;
        }
    };
    for(int i = 0; i < n; ++ i){
        if (!visit[i])
            dfs(i, 0);
    }
    cout << ans << '\n';

    return 0;
}



也可以用\(dp\),设 \(dp[i]\)表示从该节点往下,保险还能有效多少代子孙,很显然从父亲转移过来即可。因此从根直接 \(DFS\)求该 \(dp\)值即可。


F - Box in Box (abc309 F)

题目大意

给定\(n\)个三元组,可以交换三元组的元素的顺序。

问是否存在两个三元组,其一个三元组的三个元素都严格大于另一个三元组的三个元素。

解题思路

考虑二元组的做法。

如果不能交换,这就是一个典型的二维偏序问题。

如果能交换,枚举一下交换情况,可以发现在比较的时候,希望让最大值之间比较,最小值之间比较,而不是让一个二元组的最大值和一个二元组的最小值比较。

因此我们将二元组的元素排个序,小的在左,大的在右,然后再按第一位排序,此时可以发现用双指针法就解决了,当然也可以用二维偏序的解法比如树状数组解决。

从左到右枚举枚举一个二元组\(a_i\),然后找到第一个 \(a_j[0] > a_i[0]\),此时如果 \(\max_{k \geq j} a_k[1] > a_i[1]\),那就存在两个二元组满足上述条件了。而\(\max_{k \geq j} a_k[1]\)就是一个后缀最大值,事先预处理一下就可以 \(O(1)\)查询。

二维的解决了,考虑三维。

二维偏序转到三维偏序的一个做法是\(CDQ\)分治(然后花了几分钟学了下\(CDQ\)分治)。注意二维偏序的做法是保证一维有序的情况下处理另一维,而三维的话就是要保证两维都有序的情况下处理第三维。

如何保证两维都有序呢?首先我们对第一维排个序,这样就保证了一维有序,还有一维如何保证?如果我们对整个区间的第二维排序,那么第一维有序就不一定了。

能否不对整个区间排序?而取部分的区间?

首先对于一个区间\([l, r]\)是否存在答案,根据分治,答案的分布有三种可能,要么两个三元组都在 \([l, mid]\),要么都在 \([mid + 1, r]\),要么一个在左区间,一个在右区间,

前两种可能我们可以递归解决,对于第三种可能,我们就要找到两个区间的三元组的偏序关系,由于第一维事先排好序了,因此在总会有 \(a_i \leq a_j, i \in [l, mid], j \in [mid + 1, r]\) ,因此第一维可以忽略,此时就剩下第二维和第三维,问题就变成上述的二维问题,那么我们可以分别对\([l, mid],[mid + 1, r]\)的第二维排序,然后用上述提到的双指针法和后缀最大值解决。

但还有个问题,注意我们的第一维的关系是\(\leq\),而不是题目要求的严格小于,因此此时的第一维实际上不能忽略。万一第一维相等就不能成为答案,但由于我们对第二维排了序,因此第一维的大小是乱序的,似乎就没有什么规律。

细想一下,在之前的\(CDQ\)分治统计不严格偏序数量时,答案的正确性在于第一维关系是\(\leq\),符合题目要求,就可以忽略这一维,而此处的 \(\leq\)并不符合题目要求,不能忽略第一维,如果强行忽略,答案可能会不正确(考虑答案的范围就扩大成\(a_i[0] = a_j[0], a_i[1] < a_j[1], a_i[2] < a_j[2]\)),而如果能保证 \(<\),那么答案的正确性就能保证。为了保证有\(<\),考虑能否可以将\(mid\)设为 \(a_{mid + 1}[0] > a_{mid}[0]\)的地方?

但注意到, \(CDQ\)分治的时间复杂度是 \(O(n\log^2 n)\),其每次分治时的\(mid = \frac{l + r}{2}\) 是一个保证复杂度的关键之处,这样才保证每次分治处理的复杂度是\(f(n) + f(\frac{n}{2}) + f(\frac{n}{2}) + ...\),而每次分治因为涉及到排序,因此 \(f(n) = n\log n\)

为了找一个\(mid\)使得 \(a_{mid + 1}[0] > a_{mid}[0]\),同时又要保证复杂度,可以大胆猜想选择\(a_{mid}[0]\)的数的最左边或最右边(因为对第一维排了序,相同数都在一起,因此分界点就从中间位置左右偏移一下,找到第一个\(\neq a_{mid}[0]\)之处作为分治的分界点。虽然每次分治的区间大小不一定减半,但从值域上感觉会减半,并大胆猜想出题人不会卡,于是按此写了写发现过了。

如何找到从中间位置左右偏移的点,对第一维排序后预处理一下每个第一维值的最小下标和最大下标即可。

神奇的代码
#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<array<int, 3>> a(n);
    for(auto &i : a){
        for(auto &j : i)
            cin >> j;
        sort(i.begin(), i.end());
    }
    sort(a.begin(), a.end(), [&](const auto& x, const auto& y){
            return x[0] < y[0];
        });
    map<int, int> posl, posr;
    for(int i = n - 1; i >= 0; -- i){
        posl[a[i][0]] = i;
    }
    for(int i = 0; i < n; ++ i){
        posr[a[i][0]] = i;
    }
    bool ok = 0;
    function<void(int, int)> solve = [&](int l, int r){
        if (l >= r - 1)
            return;
        int mid = posl[a[(l + r) >> 1][0]];
        if (mid <= l)
            mid = posr[a[(l + r) >> 1][0]] + 1;
        if (mid >= r)
            return;
        solve(l, mid);
        solve(mid, r);

        sort(a.begin() + l, a.begin() + mid, [&](const auto& x, const auto&y){
                return x[1] < y[1];
                });
        sort(a.begin() + mid, a.begin() + r, [&](const auto& x, const auto&y){
                return x[1] < y[1];
                });

        vector<int> suf(r - l);
        for(int i = r - 1; i >= l; -- i){
            suf[i - l] = a[i][2];
            if (i < r - 1)
                suf[i - l] = max(suf[i - l], suf[i - l + 1]);
        }
        int pos = mid;
        for(int i = l; i < mid; ++ i){
            while(pos < r && a[pos][1] <= a[i][1])
                ++ pos;
            if (pos == r)
                break;
            if (suf[pos - l] > a[i][2])
                ok = 1;
        }
    };
    solve(0, n);
    if (ok)
        cout << "Yes" << '\n';
    else 
        cout << "No" << '\n';

    return 0;
}



G - Ban Permutation (abc309 G)

题目大意

问关于\(n\)的排列的数量,满足 \(\forall i \in [1,n], |p_i - i| \geq x\)

解题思路

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

神奇的代码



Ex - Simple Path Counting Problem (abc309 Ex)

题目大意

<++>

解题思路

<++>

神奇的代码



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

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

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

相关文章

  • AtCoder Beginner Contest 348

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

    2024年04月08日
    浏览(42)
  • AtCoder Beginner Contest 314

    怎么好多陌生单词 审核怎么这么逆天,半小时还没审完 给定 (pi) 的值以及数 (n) ,要求保留 (n) 位小数输出,不四舍五入。 字符串形式储存然后截取末尾即可。 神奇的代码 (n) 个人玩轮盘赌游戏,简单说就是一个转盘有 (37) 个数字以及一个箭头,箭头会等概率停在某

    2024年02月13日
    浏览(40)
  • AtCoder Beginner Contest 308

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

    2024年02月11日
    浏览(30)
  • 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日
    浏览(36)
  • AtCoder Beginner Contest 321

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

    2024年02月08日
    浏览(31)
  • AtCoder Beginner Contest 335

    给定一个字符串,将最后一位改成 4 。 模拟即可。 神奇的代码 给定 (n) ,按字典序输出非负整数 (i,j,k) ,使得 (i+j+kleq n) (n) 只有 (21) ,直接 (O(n^3)) 枚举判断即可。 神奇的代码 二维网格,贪吃蛇,移动,进行 (q) 次操作,分两种 指定贪吃蛇下一步移动的方向 指定

    2024年01月19日
    浏览(32)
  • AtCoder Beginner Contest 322

    给定一个字符串,找到最先出现 ABC 的位置。 直接查找判断即可。 神奇的代码 给定字符串 s 和 t ,问 s 是不是 t 的前缀和后缀。 根据前后缀定义判断即可。这里试了下 python 神奇的代码 (n) 天,有 (m) 天会放烟花。 问每一天,距离未来最近的放烟花的天数。 两个双指针一

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

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

    2024年02月04日
    浏览(55)
  • AtCoder Beginner Contest 303

    给定两个字符串,问这两个字符串是否相似。 两个字符串相似,需要每个字母,要么完全相同,要么一个是 1 一个是 l ,要么一个是 0 一个是 o 按照题意模拟即可。 可以将全部 1 换成 l ,全部 0 换成 o ,再判断相等。 神奇的代码 给定 m 个 n 的排列,问有多少对 ((i, j),i j

    2024年02月07日
    浏览(35)
  • AtCoder Beginner Contest 330

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

    2024年02月05日
    浏览(116)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包