AtCoder Beginner Contest 220【A - G】
比賽連結:https://atcoder.jp/contests/abc220/tasks
A - Find Multiple
題意
判斷 \([a, b]\) 中是否有 \(c\) 的倍數。
- \(1 \le a \le b \le 1000\)
- \(1 \le c \le 1000\)
題解
模擬。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int a, b, c; cin >> a >> b >> c; for (int i = a; i <= b; i++) { if (i % c == 0) { cout << i << "\n"; return 0; } } cout << -1 << "\n"; return 0; }
B - Base K
題意
給出 \(k\) 進位制下的 \(a, b\) ,將它們轉換為十進位制。
- \(2 \le k \le 10\)
- \(1 \le a, b \le 10^5\) (十進位制下)
題解
模擬。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int k; cin >> k; string a, b; cin >> a >> b; int ta = 0, tb = 0; for (auto ch : a) { ta = ta * k + (ch - '0'); } for (auto ch : b) { tb = tb * k + (ch - '0'); } cout << 1LL * ta * tb << "\n"; return 0; }
C - Long Sequence
題意
將長為 \(n\) 的序列 \(a\) 重複拼接無數次,計算字首和第一次大於 \(x\) 時的位置。
- \(1 \le n \le 10^5\)
- \(1 \le a_i \le 10^9\)
- \(1 \le x \le 10^{18}\)
題解
有些類似取餘的思想。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<long long> a(n); for (int i = 0; i < n; i++) { cin >> a[i]; } for (int i = 1; i < n; i++) { a[i] += a[i - 1]; } long long x; cin >> x; long long ans = 0; ans = x / a.back() * n; x %= a.back(); for (int i = 0; i < n; i++) { if (a[i] > x) { ans += i + 1; break; } } cout << ans << "\n"; return 0; }
D - FG operation
題意
有一個長為 \(n\) 的序列 \(a\) ,每次有兩種可選操作:
- 移去最左端的兩個元素 \(x, y\) ,並在最左端插入 \((x + y)\ \%\ 10\)
- 移去最左端的兩個元素 \(x, y\) ,並在最左端插入 \((x \times y)\ \%\ 10\)
問在 \(2^{n - 1}\) 種可能情況中,最後餘下的數為 \(0,1,\dots,9\) 的情況數,答案對 \(998244353\) 取模。
- \(2 \le n \le 10^5\)
- \(0 \le a_i \le 9\)
題解
設 \(dp[i][j]\) 為前 \(i\) 個數餘下 \(j\) 的情況數。
程式碼
#include <bits/stdc++.h>
using namespace std;
constexpr int MOD = 998244353;
int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }
struct Z {
int x;
Z(int x = 0) : x(norm(x)) {}
int val() const { return x; }
Z operator-() const { return Z(norm(MOD - x)); }
Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<vector<Z>> dp(n, vector<Z> (10));
dp[0][a[0]] = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < 10; j++) {
dp[i][j * a[i] % 10] += dp[i - 1][j];
dp[i][(j + a[i]) % 10] += dp[i - 1][j];
}
}
for (int i = 0; i < 10; i++) {
cout << dp[n - 1][i].val() << "\n";
}
return 0;
}
E - Distance on Large Perfect Binary Tree
題意
在一個 \(n\) 層滿二叉樹中,計算距離為 \(d\) 的結點二元組個數,答案對 \(998244353\) 取模。
- \(2 \le n \le 10^6\)
- \(1 \le d \le 2 \times 10^6\)
題解
列舉路徑所經最高層結點的左右子路徑長度。
假設分別為 \(l, d - l\) ,那麼前 \(n - max(l, d - l)\) 層的結點都可以作為路徑所經的最高層結點。
易知在滿二叉樹中與根節點距離為 \(i\) 的結點的個數為 \(2^i\) ,且左右子樹各佔一半。
答案即:
\[\sum \limits _{l = 0} ^{d} (2^{n - max(l, d - l)} - 1) \times 2^{l - 1} \times 2^{r - 1} \]最後因為問的是二元組個數,所以需路徑個數再乘以二。
程式碼
#include <bits/stdc++.h>
using namespace std;
constexpr int MOD = 998244353;
int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }
struct Z {
int x;
Z(int x = 0) : x(norm(x)) {}
int val() const { return x; }
Z operator-() const { return Z(norm(MOD - x)); }
Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
int d;
cin >> d;
Z ans = 0;
for (int i = 0; i <= d; i++) {
int dep = n - max(i, d - i);
if (dep >= 0) {
ans += (binpow(Z(2), dep) - 1) * binpow(Z(2), max(0, i - 1)) * binpow(Z(2), max(0, d - i - 1));
}
}
ans *= 2;
cout << ans.val() << "\n";
return 0;
}
F - Distance Sums 2
題意
給出一棵有 \(n\) 個結點的樹,對於每個結點 \(i\) ,計算 \(\sum \limits _{j = 1} ^{n} dis(i, j)\) 。
- \(2 \le n \le 10^5\)
題解
換根 \(dp\) ,當根節點由父結點 \(p\) 轉移到子結點 \(u\) 時,路徑總和的變化量為 \(-sz[u] + (n - sz[u])\) ,即子結點所在子樹的結點距離都 \(-1\) ,其餘結點距離都 \(+1\) 。
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<vector<int>> G(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
cin >> u >> v;
--u, --v;
G[u].push_back(v);
G[v].push_back(u);
}
vector<int> sz(n), dis(n);
function<void(int, int)> dfs1 = [&](int u, int p) {
sz[u] = 1;
if (p != -1) {
dis[u] = dis[p] + 1;
}
for (auto v : G[u]) {
if (v != p) {
dfs1(v, u);
sz[u] += sz[v];
}
}
};
dfs1(0, -1);
vector<long long> ans(n);
ans[0] = accumulate(dis.begin(), dis.end(), 0LL);
function<void(int, int)> dfs2 = [&](int u, int p) {
if (p != -1) {
ans[u] = ans[p] - sz[u] + (n - sz[u]);
}
for (auto v : G[u]) {
if (v != p) {
dfs2(v, u);
}
}
};
dfs2(0, -1);
for (int i = 0; i < n; i++) {
cout << ans[i] << "\n";
}
return 0;
}
G - Isosceles Trapezium
題意
給出平面中 \(n\) 個不同點的整數座標 \((x_i, y_i)\) 及其權值 \(c_i\) ,計算可能由 \(4\) 個點組成的等腰梯形的最大權值。
- \(4 \le n \le 1000\)
- \(-10^9 \le x_i, y_i \le 10^9\)
- \(1 \le c_i \le 10^9\)
題解
注意到,等腰梯形由上下兩底確定,且上下兩底的中垂線重合,所以可以列舉所有的邊,表示其中垂線。
便利起見,採用法向式來表示每條邊所對應的中垂線,即:
\[a(x - x_0) + b(y - y_0) = 0 \]表示過點 \((x_0, y_0)\) 且與向量 \((a, b)\) 垂直的直線。
對於 \((x_i, y_i), (x_j, y_j)\) 兩點所在邊的中垂線,易知其過中點 \((\frac{x_i + x_j}{2}, \frac{y_i + y_j}{2})\) ,且與向量 \((x_j - x_i, y_j - y_i)\) 垂直。
為避免誤差,可將整體座標擴大 \(2\) 倍。
至此,用法向式表示出了每條邊的中垂線,為了便於對映,將向量約分後轉換為 \(x \ge 0\) 或 \(x = 0, y \ge 0\) 的形式,並將法向式化為一般式,即:
\(Ax + By + C = ax + by - (ax_0 + by_0)\)
這樣,對 \((A, B, C)\) 進行對映,便確定了每條邊的中垂線,之後再對中垂線所經不同點的權值進行對映即可。
Tips
可能存在多條邊中點及中垂線相同,此時取所有點權值中的最大值。
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> x(n), y(n), c(n);
for (int i = 0; i < n; i++) {
cin >> x[i] >> y[i] >> c[i];
x[i] *= 2, y[i] *= 2;
}
map<tuple<int, int, long long>, map<pair<int, int>, int>> mp;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int dx = x[j] - x[i], dy = y[j] - y[i];
int g = gcd(dx, dy);
dx /= g, dy /= g;
if (dx < 0 or (dx == 0 and dy < 0)) {
dx *= -1, dy *= -1;
}
auto line = make_tuple(dx, dy, -(1LL * dx * (x[i] + x[j]) + 1LL * dy * (y[i] + y[j])));
auto point = make_pair((x[i] + x[j]) / 2, (y[i] + y[j]) / 2);
mp[line][point] = max(mp[line][point], c[i] + c[j]);
}
}
long long ans = -1;
for (auto [line, point] : mp) {
vector<long long> v;
for (auto [ord, c] : point) {
v.push_back(c);
}
sort(v.begin(), v.end(), greater<>());
if ((int)v.size() >= 2) {
ans = max(ans, v[0] + v[1]);
}
}
cout << ans << "\n";
return 0;
}
參考
https://atcoder.jp/contests/abc220/editorial/2706