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
一个角为直角等价于这个夹角的两边对应的向量的内积等于 \(0\)
代码实现
#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
显然最小值可以取到 \(\sum L_i\),最大值可以取到 \(\sum R_i\)
可以取到 \(0\) 当且仅当最小值 \(\leqslant 0\) 且最大值 \(\geqslant 0\)
下面考虑如何构造一个合法解
不妨先将每个 \(x_i\) 初始化成 \(L_i\),然后考虑将总的可分配的增量 \(-\sum L_i\) 加给每个 \(x_i\),我们可以让前面的数加到 \(R_i\),如果剩余可分配的增量不够加到 \(R_i\) 的话,就直接将剩余的增量加到当前 \(x_i\) 上并结束遍历。
好像这个技巧很常见?
代码实现
#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
\(\operatorname{Dijkstra}\) 跑最短路的板题
代码实现
#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]
表示在前 \(i\) 个数中选 \(k\) 个数且最后一个数选 \(a_i\) 且倒数第 \(2\) 个数选 \(a_j\) 的合法方案数
状态数为 \(O(N^3)\),转移次数为 \(O(N)\),这里 \(N\) 比较小所以是可行的
代码实现
#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]
表示在前 \(i\) 个数中选 \(j\) 个数且最后一个数选 \(a_i\) 使得公差为 \(d\) 的合法方案数
其中公差 \(d\) 可以用 std::unordered_map
来维护
时间复杂度为 \(O(N^3)\)
代码实现
#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
注意到,对于每条边可贡献的次数的上界可以取到较小子树的大小
那么,我们只需找到树的重心,然后让每个点穿过重心进行匹配即可
特别地,如果 \(n\) 是奇数的话,那就不匹配重心
代码实现
#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上都有?
对于每个询问就是求对于 \(S\) 的后缀 \(S_1, S_2, \cdots, S_N\),从中找出有多少个后缀满足 \(T\) 是它的前缀。考虑在 \(T\) 后面加一个新的字符 ~
,得到 \(T'\),原问题等价于求满足 \(T \leqslant S_i \leqslant T'\) 的 \(i\) 的个数。
关于后缀数组的做法,就是二分出在 \(S\) 的所有后缀中满足 \(S_k \geqslant T\) 的最小的排名 \(k\),快速询问排名对应的后缀的下标就是后缀数组的强项了
就不贴代码了