当前位置:首页 > 技术 > 正文内容

AtCoder Beginner Contest 362 题解

访客 技术 2026年7月21日 1

由于今晚有其他安排,未能参加比赛,赛后补题。

A 购买钢笔

根据题目要求进行模拟即可,共有三种颜色对应的情况:

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int priceRed, priceBlue, priceGreen;
    cin >> priceRed >> priceBlue >> priceGreen;
    string selectedColor;
    cin >> selectedColor;
    
    if (selectedColor == "Red") {
        cout << min(priceBlue, priceGreen) << '\n';
    } else if (selectedColor == "Blue") {
        cout << min(priceRed, priceGreen) << '\n';
    } else {
        cout << min(priceRed, priceBlue) << '\n';
    }
    return 0;
}

B 直角三角形

给定三个平面坐标点,计算两两之间的欧氏距离平方,然后利用勾股定理判断是否构成直角三角形。直角三角形的充要条件是:某两条边的平方和等于第三条边的平方。

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int x1, y1, x2, y2, x3, y3;
    cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
    
    // 计算各边长的平方
    long long distAB = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    long long distBC = (x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2);
    long long distCA = (x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1);
    
    if (distAB + distBC == distCA || 
        distAB + distCA == distBC || 
        distBC + distCA == distAB) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
    return 0;
}

C 和为零

首先考虑极端情况:若将所有区间都取最小值,总和仍大于0,则必然无解(因为无法取到更小的负数)。同理,若将所有区间都取最大值,总和仍小于0,也无解。

对于其他情况,我们一定可以调整部分数的取值来达到和为0。这是因为当我们从全取最小值出发,如果总和小于0,可以通过将某些正数增大(或负数向0方向调整)来逐步逼近0。由于全取最大值时总和大于0,因此在调整过程中必然能恰好达到0。

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

struct Interval {
    int left, right;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    
    vector<Interval> seg(n + 1);
    vector<int> result(n + 1);
    long long minSum = 0, maxSum = 0;
    
    for (int i = 1; i <= n; i++) {
        int l, r;
        cin >> l >> r;
        seg[i] = {l, r};
        minSum += l;
        maxSum += r;
        result[i] = l;
    }
    
    // 检查无解情况
    if (minSum > 0 || maxSum < 0) {
        cout << "No\n";
        return 0;
    }
    
    cout << "Yes\n";
    
    // 调整数值使其和为0
    for (int i = 1; i <= n; i++) {
        int l = seg[i].left, r = seg[i].right;
        if (minSum < 0 && r - l > 0) {
            int adjust = min(abs(minSum), r - l);
            result[i] += adjust;
            minSum += adjust;
        }
    }
    
    for (int i = 1; i <= n; i++) {
        cout << result[i] << ' ';
    }
    return 0;
}

D 最短路径 3

典型的带节点权重的最短路径问题。每个节点除了边权外,还有点权。路径的总代价为:边权之和加上途径所有节点的点权。使用Dijkstra算法即可解决。

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

struct Edge {
    int to;
    int weight;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int nodeCount, edgeCount;
    cin >> nodeCount >> edgeCount;
    
    vector<int> nodeValue(nodeCount + 1);
    vector<vector<Edge>> graph(nodeCount + 1);
    
    for (int i = 1; i <= nodeCount; i++) {
        cin >> nodeValue[i];
    }
    
    for (int i = 0; i < edgeCount; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        graph[u].push_back({v, w});
        graph[v].push_back({u, w});
    }
    
    // Dijkstra算法
    const long long INF = 1e18;
    vector<long long> dist(nodeCount + 1, INF);
    vector<char> visited(nodeCount + 1, false);
    
    using State = pair<long long, int>;
    priority_queue<State, vector<State>, greater<State>> pq;
    
    dist[1] = nodeValue[1];
    pq.push({nodeValue[1], 1});
    
    while (!pq.empty()) {
        auto [currentDist, u] = pq.top();
        pq.pop();
        
        if (visited[u]) continue;
        visited[u] = true;
        
        for (const auto& edge : graph[u]) {
            int v = edge.to;
            long long w = edge.weight;
            long long newDist = currentDist + w + nodeValue[v];
            
            if (newDist < dist[v]) {
                dist[v] = newDist;
                pq.push({newDist, v});
            }
        }
    }
    
    for (int i = 2; i <= nodeCount; i++) {
        cout << dist[i] << " ";
    }
    return 0;
}

E 等差子序列计数

解法一:三元组扩展

定义状态dp[i][j][k]表示以a[i]和a[j]作为等差数列的前两项、长度为k的方案数。当三个数满足等差性质时(a[i] - a[l] = a[j] - a[i],其中l < i < j),可以进行状态转移:

dp[i][j][k] += dp[l][i][k-1]

为什么只需要判断三个数即可?因为若三个数构成等差,则它们蕴含的公差是确定的,较短的等差子序列必然包含在这三个数形成的等差中。

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

const int MAXN = 100;
const int MOD = 998244353;
int dp[MAXN + 1][MAXN + 1][MAXN + 1];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    for (int i = 1; i <= n; i++) {
        for (int j = i + 1; j <= n; j++) {
            dp[i][j][2] = 1;
            
            for (int len = 3; len <= n; len++) {
                for (int l = 1; l < i; l++) {
                    if (a[i] - a[l] == a[j] - a[i]) {
                        dp[i][j][len] = (dp[i][j][len] + dp[l][i][len - 1]) % MOD;
                    }
                }
            }
        }
    }
    
    for (int len = 1; len <= n; len++) {
        long long answer = 0;
        if (len == 1) {
            cout << n << ' ';
            continue;
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                answer = (answer + dp[i][j][len]) % MOD;
            }
        }
        cout << answer << ' ';
    }
    return 0;
}

解法二:公差为状态

定义状态dp[len][end][d]表示以位置end结尾、公差为d、长度为len的等差子序列个数。使用哈希表存储不同公差对应的方案数。

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

const int MAXN = 105;
const int MOD = 998244353;
map<int, int> dp[MAXN][MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    for (int i = 1; i <= n; i++) {
        dp[1][i][0] = 1;
        
        for (int j = 1; j < i; j++) {
            int diff = a[i] - a[j];
            dp[2][i][diff]++;
        }
        
        for (int len = 3; len <= n; len++) {
            for (int j = 1; j < i; j++) {
                int diff = a[i] - a[j];
                dp[len][i][diff] = (dp[len][i][diff] + dp[len - 1][j][diff]) % MOD;
            }
        }
    }
    
    for (int len = 1; len <= n; len++) {
        long long answer = 0;
        for (int i = 1; i <= n; i++) {
            for (auto& kv : dp[len][i]) {
                answer = (answer + kv.second) % MOD;
            }
        }
        cout << answer << ' ';
    }
    return 0;
}

F 树的完美匹配

考虑每条边对答案的贡献:删除某条边后,树被分成两个连通块,设其中一个块的节点数为v,则另一个块节点数为n-v。若选中的两个点分别位于两个连通块内,则它们之间的最短路径必然经过这条边,该边的贡献为min(v, n-v)。

要使总贡献最大,应选择使得两边节点数较为均衡的边,即树的重心(每个子树大小不超过n/2的节点)。当总结点数为偶数时,重心需要特殊处理。

具体实现:首先找到树的重心,然后按照DFS顺序将节点排列,依次配对i和i+n/2位置的节点。

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

const int MAXN = 1000005;

int n, root;
int subtreeSize[MAXN], maxSubtree[MAXN];
vector<int> tree[MAXN];

void findCentroid(int u, int parent) {
    subtreeSize[u] = 1;
    maxSubtree[u] = 0;
    
    for (int v : tree[u]) {
        if (v == parent) continue;
        findCentroid(v, u);
        subtreeSize[u] += subtreeSize[v];
        maxSubtree[u] = max(maxSubtree[u], subtreeSize[v]);
    }
    maxSubtree[u] = max(maxSubtree[u], n - subtreeSize[u]);
    
    if (maxSubtree[u] < maxSubtree[root]) {
        root = u;
    }
}

void collectNodes(int u, int parent, vector<int>& nodes) {
    if (u != root) nodes.push_back(u);
    for (int v : tree[u]) {
        if (v == parent) continue;
        collectNodes(v, u, nodes);
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> n;
    for (int i = 2; i <= n; i++) {
        int u, v;
        cin >> u >> v;
        tree[u].push_back(v);
        tree[v].push_back(u);
    }
    
    root = 1;
    maxSubtree[0] = INT_MAX;
    findCentroid(1, 0);
    
    vector<int> nodes;
    nodes.push_back(0);
    
    if (n % 2 == 0) {
        nodes.push_back(root);
    }
    
    collectNodes(root, 0, nodes);
    
    for (int i = 1; i <= n / 2; i++) {
        cout << nodes[i] << ' ' << nodes[i + n / 2] << '\n';
    }
    return 0;
}

G 子串查询

本题为AC自动机模板题,考察字符串模式匹配的基本应用。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。