Codeforces Round #743 (Div. 2) 題解
旅行傳送門
A. Countdown
題意:給你一個 \(n\) 位的字串。每位均為一個從 \(0\) 至 \(9\) 的整數(即整個字串表示一個從 \(0\) 至 \(10^{n-1}\) 的整數)。你可以執行以下操作之一任意次:
- 將字串表示的數字減 \(1\)
- 交換任意兩位數字
求使整個字串轉變為 \(0\) 字串所需的最少運算元。
題目分析:最優解顯然是將其它位上的數字交換至個位上再減為 \(0\) ,除了原本就在個位上的數之外,其餘各數的花費均為: \(s[i]\) (變 \(0\) 費用) + \(1\) (交換費用)。
AC程式碼:
#include <bits/stdc++.h> #define rep(i, x, y) for (register int i = (x); i <= (y); i++) #define down(i, x, y) for (register int i = (x); i >= (y); i--) #define IOS \ ios::sync_with_stdio(false); \ cin.tie(nullptr); \ cout.tie(nullptr); using ll = long long; using namespace std; ll solve() { int n; string s; cin >> n >> s; ll res = 0; for (auto x : s) if (x - '0') res += x - '0' + 1; if (s[n - 1] - '0') --res; return res; } int main(int argc, char const *argv[]) { IOS; int T; cin >> T; while (T--) cout << solve() << endl; return 0; }
B. Swaps
題意:給你兩個長度均為 \(n\) 的序列 \(a\) 和 \(b\) 。序列 \(a\) 僅包含從 \(1\) 至 \(2n\) 的每個奇數,序列 \(b\) 僅包含從 \(1\) 到 \(2n\) 的每個偶數。
你可以對這兩個序列執行以下操作:
- 選擇兩序列之一
- 選擇從 \(1\) 至 \(n−1\) 的一個索引 \(i\)
- 交換所選序列的第 \(i\) 個和第 \((i+1)\) 個元素
計算使序列 \(a\) 的字典序小於序列 \(b\) 所需的最小運算元。
題目分析:要使得字典序最小,顯然有 \(a[1] < b[1]\) 。考慮暴力的做法,列舉每個 \(a_i\)
如何優化?假設現在 \(a[1] = x\) ,那符合條件的 \(b_i\) 必為 \(x+1\) 、 \(x+3\) 、 \(x+5 \cdots 2n-2\) 、 \(2n\) 。因此我們可以用 \(pair<int,int>\) 記錄下每個元素的初始位置和元素大小,按值排序後用字尾和的思想預先處理出 \(b\) 序列中 \([x+1,2n]\) 挪至列首費用的最小值後再進行計算,時間複雜度降至 \(O(n)\) 。
AC程式碼:
#include <bits/stdc++.h> #define rep(i, x, y) for (register int i = (x); i <= (y); i++) #define down(i, x, y) for (register int i = (x); i >= (y); i--) #define pii std::pair<int, int> #define mp std::make_pair const int inf = 0x3f3f3f3f; const int maxn = 1e5 + 5; char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf; #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++) inline int read() { int x = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } int solve() { int n = read(); std::vector<pii> a(n + 1), b(n + 1); std::vector<int> mn(n + 1); rep(i, 1, n) a[i].first = read(), a[i].second = i; rep(i, 1, n) b[i].first = read(), b[i].second = i; std::sort(a.begin() + 1, a.begin() + n + 1); std::sort(b.begin() + 1, b.begin() + n + 1); mn[n] = b[n].second; down(i, n - 1, 1) mn[i] = std::min(mn[i + 1], b[i].second); int res = inf; rep(i, 1, n) res = std::min(res, a[i].second + mn[i] - 2); return res; } int main(int argc, char const *argv[]) { int T = read(); while (T--) printf("%d\n", solve()); return 0; }
C. Book
題意:給你一本有 \(n\) 章的書。每章都有一定數量的前置章節,需要理解所有的前置章節後才能理解本章的內容。現在你將從頭到尾反覆閱讀本書,直到你理解整本書。問你在閱讀多少遍後才能啃完這本書,或讀懂這本書對你來說為時尚早。
題目分析:畫下樣例不難發現是拓撲排序。套個拓撲排序的板子,當某章節入度為 \(0\) 入隊時,若其編號大於前置章節,則該章節這一輪閱讀就能讀到,否則要等到下一輪,我們可以用優先佇列維護當前可讀章節的閱讀順序。
AC程式碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pii std::pair<int, int>
#define mp std::make_pair
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int solve()
{
int n = read();
std::vector<int> in(n + 1);
std::vector<int> e[n + 1];
std::priority_queue<pii, std::vector<pii>, std::greater<pii>> q;
rep(i, 1, n)
{
int k = in[i] = read();
if (!in[i])
q.push(mp(1, i));
rep(j, 1, k) e[read()].push_back(i);
}
int res, cnt = 0;
while (!q.empty())
{
pii cur = q.top();
q.pop();
res = cur.first;
++cnt;
for (auto v : e[cur.second])
{
--in[v];
if (!in[v])
q.push(mp(v > cur.second ? cur.first : cur.first + 1, v));
}
}
if (cnt < n)
return -1;
return res;
}
int main(int argc, char const *argv[])
{
int T = read();
while (T--)
printf("%d\n", solve());
return 0;
}
D. Xor of 3
題意:給你一個長為 \(n\) 的 \(01\) 序列,你可以進行以下操作至多 \(n\) 次直至序列中所有元素均為 \(0\) :
- 從 \(1\) 到 \(n−2\) 中選擇一個索引 \(i\)
- 令 \(a_i = a_{i+1} = a_{i+2} = a_i⊕a_{i+1}⊕a_{i+2}\)
判斷是否有解,若有解,輸出任一解決方案。
題目分析:先考慮變換前後的不變數,首先每次變換均不會改變序列的奇偶性,要麼少兩個 \(1\) 要麼多兩個 \(1\) ,所以原序列若存在奇數個 \(1\) 則無解。
偶數個 \(1\) 情況下,考慮一段連續的 \(1\) :
- 如果是偶數個 \(1\) ,由於本題序列的奇偶不變性,一定可以將這段 \(1\) 全消為 \(0\)
- 如果是奇數個 \(1\) ,那麼不能靠自己消完,所以要往後每次把兩個 \(0\) 變成 \(1\) ,直到遇到 \(101\) 這種情況,把 \(101\) 變成 \(000\) 後,前面的 \(1\) 只有偶數個,即轉換為了上述情況,就可以消完了
最後特判一下 \(0、0、0 \cdots 1、1、1\) 情況就好,由於末尾段 \(1\) 的個數必為偶數個,所以前面只要存在 \(0\) 就可以全部消完,那麼唯一無解的情況下即是序列轉化為了全 \(1\) 序列(如 \([1,0,0,1]\) )。
AC程式碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void solve()
{
int n = read(), cnt = 0;
std::vector<int> a(n + 1);
std::vector<int> ans;
for (int i = 1; i <= n; ++i)
if (a[i] = read())
++cnt;
if (cnt & 1)
{
puts("NO");
return;
}
cnt = 0;
for (int i = 1; i <= n; ++i)
{
if (a[i])
++cnt;
else if (cnt & 1)
{
if (a[i + 1])
{
a[i - 1] = a[i + 1] = 0;
for (int j = i - 1; j >= i - cnt; j -= 2)
ans.push_back(j), a[j] = a[j + 1] = 0;
cnt = 0;
}
else
{
ans.push_back(i - 1);
a[i] = a[i + 1] = 1;
++cnt;
}
}
else
{
for (int j = i - 2; j >= i - cnt; j -= 2)
ans.push_back(j), a[j] = a[j + 1] = 0;
cnt = 0;
}
}
if (cnt)
{
if (cnt == n)
{
puts("NO");
return;
}
for (int i = n - cnt; i <= n - 2; i += 2)
ans.push_back(i);
}
puts("YES");
printf("%d\n", ans.size());
for (auto x : ans)
printf("%d ", x);
if (ans.size())
puts("");
}
int main(int argc, char const *argv[])
{
int T = read();
while (T--)
solve();
return 0;
}