Educational Codeforces Round 166(A-D題解)
Educational Codeforces Round 166
2024-06-03 —yimg
A
簽到題
程式碼:
#include<bits/stdc++.h>
using namespace std;
void work()
{
int n;
cin >> n;
string s;
cin >> s;
int pos = 0;
int g = 1;
for(; pos < s.length(); ++pos){
if(s[pos] >= 'a') break;
if(pos && s[pos] < s[pos - 1]){
g = 0;
}
}
int ppp = pos;
for(; pos < s.length(); ++pos){
if(s[pos] <= '9' && s[pos] >= '0') break;
if(pos != ppp && pos != s.length() && s[pos] < s[pos - 1]){
g = 0;
}
}
// cout << pos << '\n';
if((!g) || pos != s.length()){
cout << "NO\n";
}
else{
cout << "YES\n";
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
B
題意:
給定長度為n的陣列a 和 長度為n + 1的陣列b, 將a變成b,問至少幾步操作
有如下幾種操作
- 任意位置+1 或 -1
- 選擇任意元素複製到a陣列末尾
思路:
對頭n位我們需要的運算元是\(\sum_{1}^{n} \left| a_i - b_i \right|\)
對於$ b_{n+1}$ 複製要一次操作, 若之前在頭n個數執行操作1時出現過則不用多餘操作
否則取最接近的$ a_i $ 或 $ b_i $ 執行操作1
程式碼:
#include<bits/stdc++.h>
using namespace std;
void work()
{
int n;
cin >> n;
vector<int> a(n + 5);
vector<int> b(n + 5);
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int i = 1; i <= n + 1; ++i) cin >> b[i];
long long ans = 0;
int x = b[n + 1], minn = 0x3f3f3f3f;
for(int i = 1; i <= n; ++i){
ans += abs(a[i] - b[i]);
if(x >= a[i] && x <= b[i] || x >= b[i] && x <= a[i]) minn = 0;
else minn = min(abs(a[i] - x), min(abs(b[i] - x), minn));
}
cout << ans + minn + 1 << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
C
題意:
給定 $ n + m + 1$ 個數對$ { a_i, b_i } $ , 分別求去除第i個數對, 之後的貪心操作結果
貪心操作:我們選取n 個a陣列的值,m個b陣列的值,對剩餘元素按從1到n + m + 1 的序號順序進行選取,
若n 和 m 都沒有選滿則根據$ max(a_i, b_i)$ 選取,若有一邊選滿則剩下的 哪邊沒選滿選哪邊, 求這次操作選取的數的和
思路:
刪除操作會影響的只可能有特定的1個數對,即第一個選不到自身max的數對
將每個數對,我們按他們的a, b 較大值進行分組(兩組)
對於沒選滿的一邊的數對, 我們可以選其max,
對於選滿的一邊,按編號頭 k 個(k 為本組的最大選取數n or m), 一定可以選其大值,k + 2及之後的數對只能被迫選擇 , 唯一會因為刪除而改變選擇的,只有第k + 1對數
-
刪沒選滿的一邊:直接減去刪掉的數
-
刪選滿的一邊:
- 刪 k + 1 及 之後的數, 直接減去
- 刪 頭 k 個數, 將第k + 1個的選擇取反
程式碼:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
int n, m;
cin >> n >> m;
vector<int> a(n + m + 5), b(n + m + 5);
for(int i = 1; i <= n + m + 1; ++i) cin >> a[i];
for(int i = 1; i <= n + m + 1; ++i) cin >> b[i];
vector<vector<int>> f, s;
vector<int> f_id, s_id;
vector<ll> d(n + m + 5);
ll ans = 0;
for(int i = 1; i <= n + m + 1; ++i){
if(a[i] > b[i]) f.push_back({a[i], b[i]}), f_id.push_back(i);
else s.push_back({a[i], b[i]}), s_id.push_back(i);
}
int g = 1;
if(s.size() > m){
swap(f, s);
swap(f_id, s_id);
swap(n, m);
g = 0;
}
{
for(auto i : s) ans += i[g];
for(int i = 0; i < n; ++i) ans += f[i][g ^ 1];
for(int i = n; i < f.size(); ++i) ans += f[i][g];
for(int i = 0; i < s.size(); ++i)
d[s_id[i]] = ans - s[i][g];
for(int i = n; i < f.size(); ++i)
d[f_id[i]] = ans - f[i][g];
ans += f[n][g^1] - f[n][g];
for(int i = 0; i < n; ++i)
d[f_id[i]] = ans - f[i][g^1];
}
for(int i = 1; i <= n + m + 1; ++i)
cout << d[i] << " ";
cout << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}
D
題意:
能完成括號匹配的括號序列為正常括號序列,問給定正常括號序列的好子串有多少個
好子串 : 將該子串其中的所有括號翻轉(左變右,右變左)之後的括號序列依舊是正常序列
思路:
對於括號匹配問題常常可以用字首和解決問題,左括號+1, 右括號-1, 我們根據字首和統計答案,
字首和相等位置之間的左右括號數量相等,但是像是 ()並不能翻轉 , 本題多一個翻轉後有依舊保證合法的限制,分析一下限制條件
若一個括號序列是好序列則滿足:
- $ \forall i \in n, pre_i \ge 0$
- $ pre_n = 0$
若一個子串為好子串則滿足:
- $ \forall i \in \left[l, r\right], pre_{l-1} \le pre_i - pre_{l - 1}$
- $ pre_r = pre_{l-1}$
對於每個位置我們可以先把不合法的字首刪掉再進行統計,因為一個字首在一個位置不合法,則此字首在之後的位置需要從0重新統計。
程式碼:
#include<bits/stdc++.h>
using namespace std;
void work()
{
string s;
cin >> s;
map<int, int> cnt;
int b = 0;
long long ans = 0;
cnt[b] = 1;
for(auto& i : s){
b += (i == '(' ? +1 : -1);
// Flase(TLE) :
// for(auto& j : cnt){
// if(j.first * 2 < b) j.second = 0;
// else break;
// }
while((!cnt.empty()) && cnt.begin() -> first * 2 < b)
cnt.erase(cnt.begin());
ans += cnt[b]++;
}
cout << ans << '\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
work();
}