Educational Codeforces Round 166(A-D题解)
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();
}

浙公网安备 33010602011771号