AtCoder Beginner Contest 247題解
A - Move Right
題目描述:給你一個長度為\(4\)的01
串,讓你將它右移一位並將高位補0
後輸出。
思路:根據題意模擬即可
時間複雜度:\(O(1)\)
參考程式碼:
void solve() {
string s;
cin >> s;
s = '0' + s.substr(0, 3);
cout << s << '\n';
return;
}
B - Unique Nicknames
題目描述:有\(n\)個人,每個人有兩個候選的名字,問你是否存在一種分配方案,使得每個人的名字都不在其他人的候選名字中出現。
思路:考慮到資料量比較小,考慮讀入之後暴力檢驗即可。
時間複雜度:\(O(n^2|S|)\),\(|S|\)為字串的長度
參考程式碼:
void solve() { int n; cin >> n; vector<vector<string>>strs(n, vector<string>(2)); for (int i = 0; i < n; ++i) cin >> strs[i][0] >> strs[i][1]; for (int i = 0; i < n; ++i) { int cnt = 0; for (int k = 0; k <= 1; ++k) { for (int j = 0; j < n; ++j) { if (i == j) continue; if (strs[j][0] != strs[i][k] && strs[j][1] != strs[i][k]) continue; ++cnt; break; } } if (cnt == 2) { cout << "No" << '\n'; return; } } cout << "Yes" << '\n'; return; }
C - 1 2 1 3 1 2 1
題目描述:定義序列\(S_i\),其中\(S_1 = (1)\),有遞推式\(S_i = S_{i - 1}\;i\;S_{i - 1}\),現在給定\(n\),輸出\(S_n\)。
思路:根據題意模擬即可。
時間複雜度:\(O(2^n)\)
參考程式碼:
void solve() { int n; cin >> n; vector<int> res = { 1 }; for (int i = 2; i <= n; ++i) { int m = res.size(); vector<int>ans(2 * m + 1, 0); for (int k = 0; k < m; ++k) ans[k] = ans[m + k + 1] = res[k]; ans[m] = i; swap(res, ans); } for (auto&& re : res) cout << re << ' '; cout << '\n'; return; }
D - Cylinder
題目描述:有一個佇列,有\(Q\)次操作,操作有以下兩種型別:
-
1 x c
:表示向佇列中插入\(c\)個值為\(x\)的元素 -
2 c
:表示從佇列中取出\(c\)個元素並輸出這c
個元素的值的和
資料範圍:\(1 \leq Q \leq 2 \times 10^5\)。
思路:將每次插入當做一個整體儲存在佇列中,考慮到出隊的數量可能小於當前隊首的數量,所以使用雙端佇列維護即可。
時間複雜度:\(O(n)\)
參考程式碼:
void solve() {
int q;
cin >> q;
using PII = pair<int, int>;
deque<PII> deq;
while (q--) {
int op, x, c;
cin >> op;
if (op == 1) {
cin >> x >> c;
deq.push_back({ c , x });
}
else {
cin >> c;
long long res = 0;
while (true) {
auto [nums, val] = deq.front(); deq.pop_front();
if (nums < c) {
c -= nums;
res += 1ll * nums * val;
}
else {
nums -= c;
res += 1ll * c * val;
deq.push_front({ nums , val });
break;
}
}
cout << res << '\n';
}
}
return;
}
E - Max Min
題目描述:給你一個長度為\(n\)的陣列\(A\),和兩個整數\(x , y (x \leq y)\),問你有多少個區間\([L , R]\),滿足區間的最大值為\(y\),最小值為\(x\)。
思路:首先考慮到值小於\(x\)或者大於\(y\)的位置一定不能對答案做出貢獻,所以我們可以將陣列分解成最少的不重疊子串,使得對於每一段中的元素都在區間\([x , y]\)內。現在討論其中的一個子串,假設這個子串的長度為\(m\),我們使用雙指標,統計出現的數字,若\(x , y\)在區間\([lr , rs]\)內恰好都出現, 那麼此時對答案的貢獻為\(m - rs + 1\),然後我們移動左指標直到某一個臨界值不在區間內,再去移動右指標,重複即可。
時間複雜度:\(O(n)\)
參考程式碼:
void solve() {
int n, a, x, y;
cin >> n >> y >> x;
vector<int> nums;
long long res = 0;
auto cal = [&](){
int m = nums.size();
if (m == 1) return;
int rs = 1, cntx = nums[1] == x, cnty = nums[1] == y;
for (int lr = 1; lr < m; ++lr) {
while (rs < m && (cntx == 0 || cnty == 0)) {
if (++rs >= m) break;
cntx += nums[rs] == x;
cnty += nums[rs] == y;
}
res += m - rs;
cntx -= nums[lr] == x;
cnty -= nums[lr] == y;
}
return;
};
nums.push_back(0);
for (int i = 1; i <= n; ++i) {
cin >> a;
if (a >= x && a <= y) nums.push_back(a);
else {
cal();
nums.clear();
nums.push_back(0);
}
}
cal();
cout << res << '\n';
return;
}
F - Cards
題目描述:給你\(n\)張卡片,卡片的正面的數字集合是一個\(n\)的排列,卡片的背面的數字集合也是一個\(n\)的排列,問你有多少種選擇方案可以使得選出來的卡片包含有\(1 \sim n\)的所有數字,答案對998244353
取模。
思路:考慮到卡片正反面都是排列,若將數字\(i\)抽象成編號為\(i\)的頂點,對於一張卡片上的數字,假設正面為\(u\),背面為\(v\),那麼我們假定頂點對\((u , v)\)之間存在一條有向邊\(u\to v\),那麼按照這樣就可以構建成一個有向圖,考慮到是排列,那麼這個有向圖就是由一個又一個不相交的環所組成。對於一個環上的數字,任意相鄰兩個就是一張卡片,那麼要表示這個環所對應的數字集合,假設這個環上有\(m\)個頂點,即求:對於任意兩個相鄰的頂點必須選擇其中一個的方案數。假設共有\(k\)個環,第\(i\)個環的頂點數為\(V_i\),設其對答案的貢獻為\(g(V_i)\),根據乘法原理最終答案為:
\[\prod\limits_{i = 1}^{k}g(V_i) \]時間複雜度:\(O(n)\)
參考程式碼:
const int mod = 998244353;
void solve() {
int n;
cin >> n;
vector<vector<int>>f1(n + 1, vector<int>(2, 0));//選擇第一個
vector<vector<int>>f2(n + 1, vector<int>(2, 0));//不選第一個
f1[1][1] = 1;
f2[1][0] = 1;
for (int i = 2; i <= n; ++i) {
f1[i][0] = f1[i - 1][1];
f1[i][1] = (f1[i - 1][0] + f1[i - 1][1]) % mod;
f2[i][0] = f2[i - 1][1];
f2[i][1] = (f2[i - 1][0] + f2[i - 1][1]) % mod;
}
auto calCircle = [&](int m)->int {
if (m == 1) return 1;
return (1ll * f1[m][1] + f1[m][0] + f2[m][1]) % mod;
};
vector<int>adj(n + 1), a(n + 1), b(n + 1);
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) {
cin >> b[i];
adj[a[i]] = b[i];
}
vector<bool>vis(n + 1, false);
auto dfs = [&](auto&& dfs, int rt, int u)->int {
vis[u] = true;
if (u == rt) return 1;
return dfs(dfs, rt, adj[u]) + 1;
};
int res = 1;
for (int i = 1; i <= n; ++i) {
if (vis[a[i]]) continue;
int j = dfs(dfs , a[i], b[i]);
res = 1ll * res * calCircle(j) % mod;
}
cout << res << '\n';
return;
}