D. Same Count One(Polynomial Round 2022 (Div. 1 + Div. 2, Rated, Prizes!))
題意
給定 \(n\) 個長度為 \(m\) 的 01 序列,每次操作可以選擇兩個序列a1
, a2
,並選擇一個\(pos\), std::swap(a1[pos], a2[pos])
, 求是每個序列中的 \(1\) 的個數都相等所需的最小運算元。
思路
可以發現 (\(1\) 的總數 ) \(\bmod \ n \neq 0\) 時, 是無解的。
令 \(avg =\) (\(1\) 的總數 ) \(/ n\), 我們可以把這 \(n\) 個序列分為兩類,嚴格小於 \(avg\) 的 和嚴格大於 \(avg\) 的,其他的序列可以丟掉。
嚴格大於 \(avg\) 的序列都可以為 嚴格小於 \(avg\) 的序列補充 \(1\), 直到 嚴格大於 \(avg\) 的序列 \(1\) 的個數等於 \(avg\) 或者 嚴格小於 \(avg\) 的序列 \(1\) 個數等於 \(avg\)。
直接模擬即可。
實現
void solve_problem() {
int n, m;
std::cin >> n >> m;
std::vector a(n, std::vector<int>(m, 0));
int avg = 0;
for (int i = 0;i < n; i++) {
for (int j = 0; j < m; j++) {
std::cin >> a[i][j];
avg += a[i][j];
}
}
if (avg % n == 0) {
if (n == 1) {
std::cout << 0 << "\n";
return;
}
avg /= n;
std::vector<std::pair<int,int>> q1, q2;
for (int i = 0; i < n; i++) {
int cnt = 0;
for (int j = 0; j < m; j++) {
cnt += a[i][j];
}
if(cnt < avg) q1.push_back({cnt, i});
else if (cnt > avg) q2.push_back({cnt, i});
}
int ans1 = 0;
std::vector<std::array<int, 3>> ans2;
for (int i = 1; i <= n; i++) {
if (q1.empty() || q2.empty()) break;
auto [c1, i1] = q1[0];
auto [c2, i2] = q2[0];
int d = avg - c1;
for (int j = 0; j < m; j++) {
if (d == 0 || c2 == avg) {
break;
}
if (a[i2][j] == 1 && a[i1][j] == 0) {
std::swap(a[i2][j], a[i1][j]);
c1++;
c2--;
d--;
ans2.push_back({i2 + 1, i1 + 1, j + 1});
ans1++;
}
}
q1[0] = {c1, i1};
q2[0] = {c2, i2};
if (c1 == avg) q1.erase(q1.begin());
if (c2 == avg) q2.erase(q2.begin());
}
std::cout << ans1 << "\n";
for (auto [x, y, z] : ans2) {
std::cout << std::max(x, y) << " " << std::min(y, x) << " " << z << "\n";
}
} else {
std::cout << -1 << "\n";
}
}
D. Watch the Videos(2022-2023 ICPC, NERC, Southern and Volga Russian Regional Contest (Online Mirror, ICPC Rules, Preferably Teams))
題意
有 \(n\) 個大小隨意的影片和 \(1\) 個大小為 \(m\) 的磁碟,影片要下載到磁碟中才可以開始觀看,下載第 \(i\) 個影片花費 \(a_i\) 的時間,開始下載第\(i\)個影片時,磁碟中要至少有\(a_i\)的空間才可以開始,下載完成需要花費 \(1\) 的時間觀看完,看完之後影片立刻被從磁碟中刪除,求看完所有影片需要的時間。(一次只能下載一個影片,觀看影片的時候可以開始下載影片)
思路
最壞的答案(每次看完一個影片後才開始下載下一個影片), 計算方法為:
可以發現如果在觀看影片時下載影片,答案就可以 \(-1\) 。
要想使答案最小,只需要儘可能多的在觀看影片時開始下載影片即可。
假設影片序列 \(a\) 從小到大排序, 那麼可以找到一個最大的 \(pos (1\leq pos \leq n)\), 使得序列
相鄰兩個數的和小於等於 \(m\)。
按照這個序列觀看,有 \(pos - 1\) 個影片是在正在觀看影片時開始下載的。可以使答案減少 \(pos - 1\)。
\(pos\) 是滿足單調性的,因此可以二分來找到最大的 \(pos\)。
實現
void solve_problem() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);
for (auto &x : a) {
std::cin >> x;
}
std::sort(a.begin(), a.end());
auto check = [&](int x) {
int l = 0, r = x - 1;
while (l < r) {
if (a[r] > m - a[l]) return false;
r--;
l++;
}
return true;
};
int l = 1, r = n, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
std::cout << std::accumulate(a.begin(), a.end(), 0LL) + (n - ans + 1) << "\n";
}
D. Range = √Sum(Codeforces Round #836 (Div. 2))
題意
構造一個長度為 \(n\) 的陣列 \(a_1,a_2, a_3, \dots,a_n\),使 \(a_i\) 各不相同,且 \(max(a_1,a_2, a_3, \dots,a_n) - min(a_1,a_2, a_3, \dots,a_n) = \sqrt{a_1,a_2, a_3, \dots,a_n}\) 。
思路
當 \(n\) 為偶數時, 陣列為:
陣列可以被分成 \(\frac{n}{2}\) 組,每組的和都為 \(2n\)。
當 \(n\) 為奇數時,我們嘗試 \(max(a) - max(b) = n + 1\), 因此陣列的和應為 \(n^2 + 2n + 1\)。
嘗試使用 \(n - 1\) 個數分為 \(\frac{n-1}{2}\) 組,每組和為 \(2(n+1)\), 組成的陣列和為 \(n^2 - 1\)。
此時這 \(n - 1\) 個數為:
知道了最小項,最大項也可以計算出來 \(n + \frac{n - 1}{2} + 3\)。
這時陣列的和為:
距離 \(n^2 + 2n + 1\) 還需要:
我們可以讓 第 \(\frac{n - 1}{2} + 1\) 項到第 \(n - 1\) 項都 \(+1\) 來抵消掉 \(\frac{n - 1}{2}\)。
因為第 \(n - 1\) 項 \(n + \frac{n - 1}{2} + 1\) 與 第 \(n\) 項 \(n + \frac{n - 1}{2} + 3\) 相差 \(2\),所以 \(+1\) 操作不會使陣列產生重複的數。
此時我們的陣列已經構造完成:
實現
void solve_problem() {
int n;
std::cin >> n;
if (n % 2 == 0) {
for (int i = 0; i < n/2; i++) std::cout << (n/2 + i) << " ";
for (int i = 1; i <= n/2; i++) std::cout << (n + i) << " ";
std::cout << "\n";
} else {
for (int i = 1; i <= (n - 1) / 2; i++) std::cout << (n - 1) / 2 + i + 1 << " ";
for (int i = 1; i <= (n - 1) / 2; i++) std::cout << n + i + 2 << " ";
std::cout << n + (n - 1) / 2 + 3<< "\n";
}
}