AtCoder Beginner Contest 217 【A - F】
比賽連結:https://atcoder.jp/contests/abc217/tasks
A - Lexicographic Order
題意
給出兩個字串 \(s,t\) ,若 \(s\) 字典序小於 \(t\) 輸出 Yes
,否則輸出 No
。
題解
模擬。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string s, t; cin >> s >> t; cout << (s < t ? "Yes" : "No") << "\n"; return 0; }
B - AtCoder Quiz
題意
給出 ABC
、ARC
、AGC
、AHC
四個字串中的三個,輸出剩下的那一個。
題解
模擬。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int x = 'B' ^ 'R' ^ 'G' ^ 'H'; for (int i = 0; i < 3; i++) { string s; cin >> s; x ^= s[1]; } cout << 'A' << char(x) << 'C' << "\n"; return 0; }
C - Inverse of Permutation
題意
給出一個排列,一次輸出 \(1,2,\dots,n\) 的下標。
題解
模擬。
程式碼
#include <bits/stdc++.h> using namespace std; 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<int> p(n); iota(p.begin(), p.end(), 0); sort(p.begin(), p.end(), [&](int x, int y) { return a[x] < a[y]; }); for (auto i : p) { cout << i + 1 << ' '; } return 0; }
D - Cutting Woods
題意
有一根長為 \(l\) 米的木棒,從左端起每隔 \(1\) 米有一個標記,給出 \(q\) 次格式為 \((c_i, x_i)\) 詢問:
- 若 \(c_i = 1\) ,將木棒從標記 \(x_i\) 處鋸開
- 若 \(c_i = 2\) ,輸出標記 \(x_i\) 所在木棒的長度,保證不曾從 \(x_i\) 處鋸開過
題解
記錄所有被鋸開的標記,某個標記所在木棒的長度即左右最近被鋸開的兩個標記間的距離。
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int l, q;
cin >> l >> q;
set<int> st{0, l};
while (q--) {
int c, x;
cin >> c >> x;
if (c == 1) {
st.insert(x);
} else {
auto it = st.upper_bound(x);
cout << *it - *prev(it) << "\n";
}
}
return 0;
}
E - Sorting Queries
題意
開始時有一個空序列 \(a\) ,給出 \(q\) 次詢問:
1 x
,將 \(x\) 新增至 \(a\) 末尾2
,輸出序列 \(a\) 首部的元素3
,將序列 \(a\) 排為升序
題解
利用 queue
和 priority_queue
模擬。
1 x
,將 \(x\) 新增至 queue
末尾
2
,輸出 priority_queue
的首部,若其為空,輸出 queue
的首部
3
,把 queue
中的所有元素彈入 priority_queue
中
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
priority_queue<int, vector<int>, greater<int>> pque;
queue<int> que;
int q;
cin >> q;
while (q--) {
int op;
cin >> op;
if (op == 1) {
int x;
cin >> x;
que.push(x);
} else if (op == 2) {
if (not pque.empty()) {
cout << pque.top() << "\n";
pque.pop();
} else {
cout << que.front() << "\n";
que.pop();
}
} else {
while (not que.empty()) {
pque.push(que.front());
que.pop();
}
}
}
return 0;
}
F - Make Pair
題意
有 \(2n\) 個學生站為一排,編號為 \(1,2,\dots,2n\) ,給出這 \(2n\) 個學生中的 \(m\) 對好關係。
老師想要進行 \(n\) 次操作:
- 選擇兩個相鄰且為好關係的學生,將他們移去,若留下間隙,兩邊的學生保持原次序合併
計算老師有多少種方案來進行這 \(n\) 次操作。
題解
區間 \(dp\) 。
設 \(dp[l][r]\) 為移去區間 \([l, r]\) 所需進行的 \(\frac{(r - l + 1)}{2}\) 次操作的方案數,顯然區間長度 \((r - l + 1)\) 須為 \(2\) 的倍數。
對於較大區間 \([l, r]\) 的更新,可以通過列舉與 \(l\) 配對的點 \(x\) 來進行,若 \((l, x)\) 為好關係,則有狀態轉移方程:
\[dp[l][r] = dp[l + 1][x - 1] \cdot dp[x + 1][r] \cdot C_{p + q}^{p} \]其中, \(p = \frac{(x - 1) - (l + 1) + 1}{2} + 1\) , \(q = \frac{r - (x + 1) + 1}{2}\) ,即區間 \([l, x]\) 和 \([x + 1, r]\) 各自需要操作的次數。
因為移去 \((l, x)\) 一定會先移去它們間的 \([l + 1, x- 1]\) ,所以 \(p\) 次操作的方案數為 \(dp[l + 1][x - 1]\) , \(q\) 次操作的方案數為 \(dp[x + 1][r]\) ,剩下所需決定的即 \(p + q\) 次操作的順序關係。
因為方案數已包含在 \(dp\) 中,所以 \(p\) 次操作兩兩無差別, \(q\) 次操作兩兩無差別,答案即 \(C_{p + q}^{p}(C_{p + q}^{q})\) ,含義為從總共 \(p + q\) 個位置中選擇 \(p(q)\) 個放置 \(p(q)\) 次操作。
程式碼
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 405;
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; }
};
struct Combination {
vector<Z> fac, inv;
Combination(int n) : fac(n), inv(n) {
fac[0] = 1;
for (int i = 1; i < n; i++) fac[i] = fac[i - 1] * i;
inv[n - 1] = fac[n - 1].inv();
for (int i = n - 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1);
}
Z C(int n, int m){
if(m < 0 or m > n) return 0;
return fac[n] * inv[m] * inv[n - m];
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
n *= 2;
vector<vector<int>> edge(N, vector<int> (N));
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
edge[a][b] = true;
}
vector<vector<Z>> dp(N, vector<Z> (N));
Combination C(N);
for (int l = n; l >= 1; l--) {
for (int r = l + 1; r <= n; r += 2) {
for (int x = l + 1; x <= r; x += 2) {
if (edge[l][x]) {
Z cur = 1;
if (l + 1 <= x - 1) {
cur *= dp[l + 1][x - 1];
}
if (x + 1 <= r) {
cur *= dp[x + 1][r];
}
int p = (l + 1 <= x - 1 ? ((x - 1) - (l + 1) + 1) / 2 : 0) + 1;
int q = (x + 1 <= r ? (r - (x + 1) + 1) / 2 : 0);
cur *= C.C(p + q, p);
dp[l][r] += cur;
}
}
}
}
cout << dp[1][n].val() << "\n";
return 0;
}
參考
https://atcoder.jp/contests/abc217/submissions/25575778