Codeforces Round #680 (Div. 2, based on Moscow Team Olympiad)【ABCD】
阿新 • • 發佈:2020-11-04
比賽連結:https://codeforces.com/contest/1445
A. Array Rearrangment
題意
給定兩個大小均為 \(n\) 的升序陣列 \(a\) 和 \(b\) ,判斷能否重排陣列 \(b\) 使得對任意 \(i\) 均滿足 \(a_i + b_i \le x\) 。
題解一
因為 \(a\) 為升序,所以將 \(b\) 按照降序排列判斷即可。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, x; cin >> n >> x; vector<int> a(n); for (auto &x : a) cin >> x; vector<int> b(n); for (auto &x : b) cin >> x; sort(b.begin(), b.end(), greater<>()); bool ok = true; for (int i = 0; i < n; i++) if (a[i] + b[i] > x) ok = false; cout << (ok ? "Yes" : "No") << "\n"; } return 0; }
題解二
若 \(a\) 不以升降序給出,則需要對 \(a\) 中的每個數在 \(b\) 中貪心查詢最匹配的數。
程式碼
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, x; cin >> n >> x; vector<int> a(n); for (auto &x : a) cin >> x; multiset<int> st; for (int i = 0; i < n; i++) { int x; cin >> x; st.insert(x); } for (const auto &i : a) { auto it = st.upper_bound(x - i); if (it != st.begin() and *prev(it) <= x - i) st.erase(prev(it)); } cout << (st.size() == 0 ? "Yes" : "No") << "\n"; } return 0; }
B. Elimination
題意
決賽之前有兩場選拔賽,已知:
- 第一場選拔賽第 \(100\) 名的分數為 \(a\) ,第二場選拔賽所有人的分數都不少於 \(b\)
- 第二場選拔賽第 \(100\) 名的分數為 \(c\) ,第一場選拔賽所有人的分數都不少於 \(d\)
計算兩場選拔賽的總分至少要多少才能進入決賽。
題解
假如剛好有兩百人,那麼可以進行以下合理假設:
- 第一場前一百名分數為 \(a\) ,後一百名分數為 \(b\)
- 第二場前一百名分數為 \(c\) ,後一百名分數為 \(d\)
- 第二場的前一百名剛好為第一場的後一百名
- 第二場的後一百名剛好為第一場的前一百名
那麼答案即 \(max(a + b,\ c + d)\)
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
cout << max(a + b, c + d) << "\n";
}
return 0;
}
C. Division
題意
給定兩個數 \(a\) 和 \(b\) ,計算滿足以下條件的 \(x\) 的最大值:
- \(x\) 是 \(a\) 的因子
- \(x\) 不是 \(b\) 的倍數
題解
最理想的 \(x\) 是 \(a\) 本身,此時如果 \(x\) 是 \(b\) 的倍數,那麼只要將 \(x\) 內的某一質因子降到 \(b\) 中的冪次以下即可。
程式碼
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
long long a, b;
cin >> a >> b;
vector<int> p;
long long n = b;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
p.push_back(i);
while (n % i == 0) n /= i;
}
}
if (n > 1) p.push_back(n);
long long ans = 1;
for (auto i : p) {
long long res = a;
while (res % i == 0) res /= i;
ans = max(ans, res);
}
cout << ans << "\n";
}
return 0;
}
D. Divide and Sum
題意
給定一個大小為 \(2n\) 的陣列,考慮將其拆分為兩個等長的子序列 \(p\) 和 \(q\) ,並將 \(p\) 以升序排列, \(q\) 以降序排列,同時定義 \(f(p, q) = \sum_{i = 1}^n |p_i - q_i|\) 。
計算所有可能子序列情況下的 \(f(p, q)\) 之和。
題解
不管怎麼分,都是大的一半減去小的一半,將和記為 \(sum\) ,答案即 \(C_{2n}^n \times sum\) 。
程式碼
#include <bits/stdc++.h>
#define int long long
using namespace std;
constexpr int N = 1e6 + 100;
constexpr int MOD = 998244353;
int fac[N], inv[N];
int binpow(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = 1LL * res * a % MOD;
a = 1LL * a * a % MOD;
b >>= 1;
}
return res;
}
int C(int n, int m){
if(m < 0 or m > n) return 0;
return 1LL * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
void Init(){
fac[0] = 1;
for (int i = 1; i < N; i++) fac[i] = 1LL * fac[i - 1] * i % MOD;
inv[N - 1] = binpow(fac[N - 1], MOD - 2);
for (int i = N - 2; i >= 0; i--) inv[i] = 1LL * inv[i + 1] * (i + 1) % MOD;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
Init();
int n;
cin >> n;
n *= 2;
vector<int> a(n);
for (auto &x : a) cin >> x;
sort(a.begin(), a.end());
int sum = 0;
for (int i = 0; i < n; i++) {
sum += abs(a[i] - a[n - 1 - i]);
sum %= MOD;
}
cout << C(n, n / 2) * sum % MOD << "\n";
return 0;
}