首页 > 其他分享 >AtCoder Beginner Contest 348 A-F 题解

AtCoder Beginner Contest 348 A-F 题解

时间:2024-04-09 12:23:02浏览次数:17  
标签:AtCoder int 题解 sum cin vector auto 348 dis

A - Penalty Kick

Question

高桥将在一场足球比赛中获得 \(N\) 个点球。

对于第 \(i\) 个罚球,如果 \(i\) 是 \(3\) 的倍数,他将罚球失败,否则罚球成功。

请打印他罚球的结果。

Solution

当 \(i \% 3 == 0\) 时说明能被 \(3\) 整除

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n; cin >> n;
    for (int i = 1; i <= n; i++)
        if (i % 3 == 0)
            putchar('x');
        else
            putchar('o');
    return 0;
}

B - Farthest Point

Question

在 \(xy\) (平面)上有 \(N\) 个点,其 ID 编号从 \(1\) 到 \(N\) 。点 \(i\) 位于坐标 \((X_i, Y_i)\) 处,没有两个点的坐标相同。

从每个点出发,找出最远的点并打印其 ID 编号。如果多个点都是最远点,则打印这些点中最小的 ID 号。

这里我们使用欧氏距离:对于两个点 \((x_1,y_1)\) 和 \((x_2,y_2)\) ,它们之间的距离是 \(\sqrt{(x_1-x_2)^{2}+(y_1-y_2)^{2}}\)

Solution

发现 \(n\) 很小

可以暴力枚举每个 \(i\) ,求出与 \(i\) 的欧几里得距离最大的 \(j\),并输出

注意使用 sqrt() 会产生精度问题,建议不开平方比较

Code

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
int main() {
    int n; cin >> n;
    vector<pii> a(n);
    for (int i = 0; i < n; i++) 
        cin >> a[i].first >> a[i].second;
    for (int i = 0; i < n; i++) {

        auto dist = [&](pii a, pii b) {
            return (a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second);
        };

        int idx = -1;
        int max_x = -1;
        for (int j = 0; j < n; j++) {
            if (i == j) continue;
            if (max_x < dist(a[i], a[j])) {
                max_x = dist(a[i], a[j]);
                idx = j;
            }
        }
        cout << idx + 1 << '\n';
    }
    return 0;
}

C - Colorful Beans

Question

豆子有 \(N\) 个,其中 \(i\) 个豆子的美味度为 \(A_i\) ,颜色为 \(C_i\) 。豆子是混合的,只能通过颜色来区分。

您将选择一种颜色的豆子,并吃一颗这种颜色的豆子。

通过选择最佳颜色,最大限度地减少所吃豆子的美味度。

Solution

利用 map<int,int> 记录每种颜色的种子的美味度的最小值

最后遍历 map,找到最大的美味度最小值

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n; cin >> n;
    map<int, int> p;
    for (int i = 0; i < n; i++) {
        int A, C; cin >> A >> C;
        if (!p.count(C)) p[C] = A;
        else p[C] = min(p[C], A);
    }
    int ans = 0;
    for (auto [a, c] : p) ans = max(ans, c);
    cout << ans << '\n';
    return 0;
}

D - Medicines on Grid

Question

有一个网格,网格中有 \(H\) 行和 \(W\) 列。让 \((i, j)\) 表示从上往下第 \(i\) 行,从左往上第 \(j\) 列的单元格。每个单元格的状态由字符 \(A_{i,j}\) 表示,其含义如下:

  • . : 空单元格。
  • # : 一个障碍物。
  • S : 空单元格和起点。
  • T : 空单元格和目标点。

高桥可以通过消耗 \(1\) 能量从当前单元格移动到垂直或水平相邻的空单元格。如果能量为 \(0\) ,则无法移动,也无法离开网格

网格中有 \(N\) 种药物。 \(i\) -th药品位于空格 \((R_i, C_i)\) ,可以用来将能量设置为 \(E_i\) 。注意,能量并不一定会增加。他可以在当前格子中使用药物。用过的药会消失

高桥以 \(0\) 的能量从起点开始,并希望到达目标点。请判断这是否可行

Solution

考虑按照药物作为点建新图,如果一个药物 \(i\) 能在 \(E_i\) 步内走到 \(j\),则建一条 \(i\Rightarrow j\) 的边

然后在新图上跑 bfs 查看起始点和终点是否能走到

Code

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int inf = 1e9;
const int flg[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
struct Node {
    pii pos; int val;
};

int main() {
    int H, W; cin >> H >> W;
    pii st, ed;
    vector<vector<char> > mp(H + 2, vector<char>(W + 2, '#'));
    
    for (int i = 1; i <= H; i++) {
        for (int j = 1; j <= W; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == 'S') st = {i, j};
            if (mp[i][j] == 'T') ed = {i, j};
        }
    }
    int N, id_s = 0; cin >> N;
    vector<Node> a(N + 2);
    
    for (int i = 1; i <= N; i++) {
        cin >> a[i].pos.first >> a[i].pos.second >> a[i].val;
        if (a[i].pos == st) id_s = i;
    }
    a[N + 1].pos = ed; a[N + 1].val = 0;

    vector<vector<int> > g(N + 2,vector<int>());
    for (int i = 1; i <= N; i++) {
        auto bfs = [&] (pii s) {
            vector<vector<int> > dis (H + 2, vector<int>(W + 2, -1));
            queue<pii> q;
            q.push(s);
            dis[s.first][s.second] = 0;
            while (!q.empty()) {
                auto [x, y] = q.front(); q.pop();
                for (int k = 0; k < 4; k++) {
                    int nx = x + flg[k][0], ny = y + flg[k][1];
                    if (mp[nx][ny] == '#' || dis[nx][ny] != -1) continue;
                    dis[nx][ny] = dis[x][y] + 1;
                    q.push({nx, ny});
                }
            }
            return dis;
        };
        auto dis = bfs(a[i].pos);
        for (int j = 1; j <= N + 1; j++) {
            if (i == j) continue;
            auto [x, y] = a[j].pos;
            if (dis[x][y] != -1 && dis[x][y] <= a[i].val)
                g[i].push_back(j);
        }
    }
    if (id_s == 0) return cout << "No" << '\n', 0;
    vector<int> dis(N + 2, -1);
    queue<int> q; q.push(id_s); dis[id_s] = 0;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (auto v : g[u]) {
            if (dis[v] != -1) continue;
            dis[v] = dis[u] + 1;
            q.push(v);
            if (v == N + 1) return cout << "Yes" << '\n', 0;
        }
    }
    return cout << "No" << '\n', 0;
}

E - Minimize Sum of Distances

Question

给你一棵有 \(N\) 个顶点的树。顶点编号为 \(1\) 到 \(N\) , \(i\) -th 边连接顶点 \(A_i\) 和 \(B_i\) 。

我们还给出了一个长度为 \(N\) 的正整数序列 \(C = (C_1, C_2, \ldots ,C_N)\) 。设 \(d(a, b)\) 是顶点 \(a\) 和 \(b\) 之间的边数,而对于 \(x = 1, 2, \ldots, N\) , 设为 \(\displaystyle f(x) = \sum_{i=1}^{N} (C_i \times d(x, i))\) 。求 \(\displaystyle \min_{1 \leq v \leq N} f(v)\)

Solution

非常显然的换根 DP

先第一次求出 \(f(root)\) 考虑从根节点向下遍历

假设已经知道了一个节点 \(f(u)\),求 \(u\) 的一个儿子 \(v\)

那么 \(f(v)=f(u)-\sum\limits_{i\in S} C_i+\sum\limits_{i\notin S} C_i\)

其中 \(S\) 为 \(v\) 的后代节点

\(\sum\limits_{i\in S} C_i\) 和 \(\sum\limits_{i\notin S} C_i\) 可以预处理得出,所以转移的时间复杂度是 \(O(1)\) 的

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 1e18;
int main() {
    int N; cin >> N;
    vector<vector<int> > g(N + 1, vector<int>());
    vector<ll> c(N + 1); ll c_sum = 0;
    for (int i = 1; i < N; i++) {
        int u, v; cin >> u >> v;
        g[u].push_back(v); g[v].push_back(u);
    }
    for (int i = 1; i <= N; i++) {
        cin >> c[i]; c_sum += c[i];
    }
    vector<ll> dp(N + 1, 0);
    vector<ll> sz(N + 1, 0), dis(N + 1, 0);
    function<void(int, int)> dfs_1 = [&] (int u, int fa) {
        sz[u] = c[u];
        for (auto v : g[u]) {
            if (v == fa) continue;
            dis[v] = dis[u] + 1;
            dfs_1(v, u);
            sz[u] += sz[v];
        }
    };
    dfs_1(1, 0);
    for (int i = 1; i <= N; i++)
        dp[1] += c[i] * dis[i];
    function<void(int, int)> dfs_2 = [&] (int u, int fa) {
        for (auto v : g[u]) {
            if (v == fa) continue;
            dp[v] = dp[u] - sz[v] + (c_sum - sz[v]);
            dfs_2(v, u);
        }
    };
    dfs_2(1, 0);
    ll ans = *min_element(dp.begin() + 1, dp.end());
    cout << ans << '\n';
    return 0;
}

F - Oddly Similar

Question

有长度为 \(M\) 的 \(N\) 个序列,表示为 \(A_1, A_2, \ldots, A_N\) 。长度为 \(i\) 的序列由 \(M\) 个整数 \(A_{i,1}, A_{i,2}, \ldots, A_{i,M}\) 表示。

长度为 \(M\) 的两个序列 \(X\) 和 \(Y\) 如果且仅当 \(X_i = Y_i\) 的索引 \(i (1 \leq i \leq M)\) 的个数为奇数时,才说这两个序列相似。

求满足 \(1 \leq i < j \leq N\) 且 \(A_i\) 和 \(A_j\) 相似的整数对 \((i,j)\) 的个数。

Solution

听说这题直接暴力 + O3 优化就能跑过,atcoder 真是神机

我介绍一种 bitset 的做法

定义 vector<bitset<2001> > p(n + 1) 如果 \(p[i][j]=1\) 说明第 \(i\) 行和第 \(j\) 行是相似的

所以对于每一列,我们可以利用 bitset 的异或操作快速处理出每行之间的关系

具体实现就是对于一列,求出这一列中相同数字的行号,放在一个 vector<int> 中,对于 vector<int> 的每个位置,我们都把对应位置的 bitset 都标记为 \(1\),最后对于 vector<int> 中的每个数,利用 p[k] ^= now 来更新 \(p[k]\)

Code

#include <bits/stdc++.h>
using namespace std;

int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int main() {
    freopen ("F.in", "r", stdin);
    int n, m;  n = read(); m = read();
    vector<vector<int> > a(n + 1, vector<int>(m + 1));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            a[i][j] = read();
    vector<bitset<2001> > p(n + 1);
    vector<vector<int> > vc(1000 , vector<int>());
    for (int i = 1; i <= m; i++) {
        for (auto &x : vc) x.clear();
        for (int j = 1; j <= n; j++)
            vc[a[j][i]].push_back(j);
        for (auto &x : vc) if (x.size() > 0) {
            bitset<2001> now(0);
            for (auto &k : x) now[k] = 1;
            for (auto &k : x) p[k] ^= now;
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++) 
            ans += p[i][j];
    cout << ans << '\n';
    return 0;
}

标签:AtCoder,int,题解,sum,cin,vector,auto,348,dis
From: https://www.cnblogs.com/martian148/p/18123688

相关文章

  • SP30785的题解
    (一)树链剖分板子题。支持单点取反,区间查询。在线段树的每一个节点上分别记录全为\(1\)或全为\(0\)。代码挺好理解的。(二)AC代码。#include<bits/stdc++.h>usingnamespacestd;constintmxn=3e5+10;intn,q,head[mxn],cnt,id[mxn],cntt,fa[mxn],cnt1,son[mxn],siz[m......
  • 【蓝桥·算法双周赛 第 9 场 小白入门赛】字符迁移【算法赛】题解(字符串+模运算+差分)
    思路差分数组是一种特殊的数组,它的第iii个数定义为原数组的第ii......
  • 【蓝桥·算法双周赛 第 4 场 小白入门赛】自助餐【算法赛】题解(分支+字符串)
    思路首先定义一个整型变量n和一个长整型变量ans,其中n用于存放输入的字符串个数,ans则用于累计所有字符串对应的价格。在接收到n之后,进入一个循环,在循环中,每次接收一个字符串s,并根据s的首字母判断该字符串对应的餐盘种类,并将其价格累加到ans中。具体来说,如果......
  • AtCoder Beginner Contest 348
    地址。赛时情况A、B题都很显然,C题大概推了好一会儿,最后还是做出来了。D题感觉十分难做,估计很难写,看了E。感觉还是不会,听说是原题,搜了一下,发现是树的重心,我还不会。直接贺题解,发现不对。修改了一下还是不对,最后发现INF取小了,过了。后面的不看了。赛后总结还行,跳过D......
  • newstart 部分题解和pwn相关的学习
    做newstart的pwnpieee题的pie的学习首先:对于pieee这道题很简单的栈溢出,除了NX其他的保护都开了,然后呢在左边也发现了后门函数相对偏移为0x1264(对于这里我们只用关心后三位,因为pie不会随机化地址的低12位,通俗点说就是我们十六进制地址的后三位)而一般而言后三位的地址能够确定我......
  • 【题解】洛谷马的遍历
    马的遍历方法:广度优先搜索源代码如下#include<iostream>#include<queue>#include<cstdio>#include<cstring>usingnamespacestd;structcoord{//结构体定义x,y坐标intx,y;};queue<coord>Q;intans[500][500];//-1代表未访问intwalk[8......
  • Leetcode 第 390 场周赛题解
    Leetcode第390场周赛题解Leetcode第390场周赛题解题目1:3090.每个字符最多出现两次的最长子字符串思路代码复杂度分析题目2:3091.执行操作使数据元素之和大于等于K思路代码复杂度分析题目3:3092.最高频率的ID思路代码复杂度分析题目4:3093.最长公共后缀查询思......
  • CF1361E James and the Chase 题解
    Description给定一个有\(n\)个点\(m\)条边的有向强连通图。称一个点是好的当且仅当它到其他点都有且只有一条简单路径。如果好的点至少有\(20\%\)则输出所有好的点,否则输出-1。单个测试点内有多组数据。\(1\leqT\leq2\times10^3,1\leqn\leq10^5,1\leqm\leq2\time......
  • 任务处理【华为OD机试】(JAVA&Python&C++&JS题解)
    一.题目-任务处理在某个项目中有多个任务(用tasks数组表示)需要您进行处理,其中tasks[i]=[si,ei],你可以在si<=day<=ei中的任意一天处理该任务。请返回你可以处理的最大任务数。注:一天可以完成一个任务的处理。输入描述:第一行为任务数量n,1<=n<=100000。后......
  • 跳马【华为OD机试】(JAVA&Python&C++&JS题解)
    一.题目马是象棋(包括中国象棋和国际象棋)中的棋子,走法是每步直一格再斜一格,即先横着或直着走一格,然后再斜着走一个对角线,可进可退,可越过河界,俗称“马走‘日’字。给顶m行n列的棋盘(网格图),棋盘上只有有棋子象棋中的棋子“马”,并且每个棋子有等级之分,等级为k的马可以跳1~k......