A. Buy a Pen
模拟
代码实现
a = list(map(int, input().split())) c = input() a.pop(['Red', 'Green', 'Blue'].index(c)) print(min(a))
B. Right Triangle
一个角为直角等价于这个夹角的两边对应的向量的内积等于
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; struct Vector { int x, y; Vector(int x=0, int y=0): x(x), y(y) {} Vector operator-(const Vector& o) const { return Vector(x-o.x, y-o.y); } int dot(const Vector& v) { return x*v.x + y*v.y; } }; int main() { Vector a, b, c; cin >> a.x >> a.y; cin >> b.x >> b.y; cin >> c.x >> c.y; bool ok = false; rep(i, 3) { if ((b-a).dot(c-a) == 0) ok = true; swap(a, b); swap(b, c); } if (ok) puts("Yes"); else puts("No"); return 0; }
C. Sum = 0
显然最小值可以取到 ,最大值可以取到
可以取到 当且仅当最小值 且最大值
下面考虑如何构造一个合法解
不妨先将每个 初始化成 ,然后考虑将总的可分配的增量 加给每个 ,我们可以让前面的数加到 ,如果剩余可分配的增量不够加到 的话,就直接将剩余的增量加到当前 上并结束遍历。
好像这个技巧很常见?
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; int main() { int n; cin >> n; vector<int> l(n), r(n); rep(i, n) cin >> l[i] >> r[i]; ll sumL = 0, sumR = 0; rep(i, n) sumL += l[i]; rep(i, n) sumR += r[i]; if (sumL > 0 or sumR < 0) { puts("No"); return 0; } puts("Yes"); vector<int> ans = l; ll rem = -sumL; rep(i, n) { ll canAdd = r[i]-l[i]; if (canAdd < rem) { ans[i] = r[i]; rem -= canAdd; } else { ans[i] += rem; break; } } rep(i, n) cout << ans[i] << ' '; return 0; }
D. Shortest Path 3
跑最短路的板题
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; using P = pair<ll, int>; struct Edge { int to, cost; Edge(int to, int cost): to(to), cost(cost) {} }; int main() { int n, m; cin >> n >> m; vector<int> a(n); rep(i, n) cin >> a[i]; vector<vector<Edge>> g(n); rep(i, m) { int u, v, b; cin >> u >> v >> b; --u; --v; g[u].emplace_back(v, b+a[v]); g[v].emplace_back(u, b+a[u]); } const ll INF = 1e18; vector<ll> dist(n, INF); priority_queue<P, vector<P>, greater<P>> q; dist[0] = a[0]; q.emplace(a[0], 0); while (q.size()) { auto [d, v] = q.top(); q.pop(); if (dist[v] != d) continue; for (auto [u, w] : g[v]) { ll nd = d+w; if (dist[u] <= nd) continue; dist[u] = nd; q.emplace(nd, u); } } for (int i = 1; i < n; ++i) cout << dist[i] << ' '; return 0; }
E. Count Arithmetic Subsequences
记 dp[i][j][k]
表示在前 个数中选 个数且最后一个数选 且倒数第 个数选 的合法方案数
状态数为 ,转移次数为 ,这里 比较小所以是可行的
代码实现
#include <bits/stdc++.h> #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #endif #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using mint = modint998244353; mint dp[81][81][81]; int main() { int n; cin >> n; vector<int> a(n); rep(i, n) cin >> a[i]; rep(i, n) { rep(j, i) dp[i][j][2] = 1; rep(j, i)rep(k, n) { mint now = dp[i][j][k]; if (now == 0) continue; for (int x = i+1; x < n; ++x) { if (a[x]-a[i] == a[i]-a[j]) { dp[x][i][k+1] += now; } } } } vector<mint> ans(n+1); ans[1] = n; for (int k = 2; k <= n; ++k) { rep(i, n)rep(j, i) ans[k] += dp[i][j][k]; } for (int i = 1; i <= n; ++i) cout << ans[i].val() << ' '; return 0; }
也有另一种dp做法
记 dp[i][j][d]
表示在前 个数中选 个数且最后一个数选 使得公差为 的合法方案数
其中公差 可以用 std::unordered_map
来维护
时间复杂度为
代码实现
#include <bits/stdc++.h> #if __has_include(<atcoder/all>) #include <atcoder/all> using namespace atcoder; #endif #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using mint = modint998244353; unordered_map<int, mint> dp[81][81]; int main() { int n; cin >> n; vector<int> a(n); rep(i, n) cin >> a[i]; rep(ni, n)rep(i, ni) { int d = a[ni]-a[i]; dp[ni][2][d] += 1; rep(j, n) dp[ni][j+1][d] += dp[i][j][d]; } vector<mint> ans(n+1); ans[1] = n; for (int j = 2; j <= n; ++j) { rep(i, n) { for (auto [d, x] : dp[i][j]) ans[j] += x; } } for (int i = 1; i <= n; ++i) cout << ans[i].val() << ' '; return 0; }
F. Perfect Matching on a Tree
注意到,对于每条边可贡献的次数的上界可以取到较小子树的大小
那么,我们只需找到树的重心,然后让每个点穿过重心进行匹配即可
特别地,如果 是奇数的话,那就不匹配重心
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; int main() { int n; cin >> n; vector<vector<int>> to(n); rep(i, n-1) { int a, b; cin >> a >> b; --a; --b; to[a].push_back(b); to[b].push_back(a); } int centroid = 0; vector<int> sz(n); auto dfs = [&](auto& f, int v, int p=-1) -> int { int mx = 0; sz[v] = 1; for (int u : to[v]) { if (u == p) continue; sz[v] += f(f, u, v); mx = max(mx, sz[u]); } mx = max(mx, n-sz[v]); if (mx*2 <= n) centroid = v; return sz[v]; }; dfs(dfs, 0); vector<int> vs; auto dfs2 = [&](auto& f, int v, int p=-1) -> void { for (int u : to[v]) { if (u == p) continue; f(f, u, v); } vs.push_back(v); }; dfs2(dfs2, centroid); if (n%2 == 1) vs.pop_back(); rep(i, n/2) { int a = vs[i], b = vs[i+n/2]; cout << a+1 << ' ' << b+1 << '\n'; } return 0; }
G. Count Substring Query
板题,几乎在任何oj上都有?
对于每个询问就是求对于 的后缀 ,从中找出有多少个后缀满足 是它的前缀。考虑在 后面加一个新的字符 ~
,得到 ,原问题等价于求满足 的 的个数。
关于后缀数组的做法,就是二分出在 的所有后缀中满足 的最小的排名 ,快速询问排名对应的后缀的下标就是后缀数组的强项了
就不贴代码了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!