Educational Codeforces Round 168 (Rated for Div. 2)(A-E)

比赛链接:https://codeforces.com/contest/1997

A. Strong Password

题意

给一个字符串 s s s

输入 s s s 需要的总时间计算如下:
1,第一个字符时间为 2 2 2 秒;
2,其余字符如果和前一个字符相同,输入时间为 1 1 1 秒,否则为 2 2 2 秒。

问在 s s s 的任意位置插入任意一个字母后,输入时间最大的字符串是什么?

数据范围

1 ≤ ∣ s ∣ ≤ 10 1 \le |s| \le 10 1s10

思路

字符串的长度不大,所以可以枚举在字符串的哪个位置插入了哪个字符,然后计算对应的输入时间,保留输入时间最大的字符串即为答案。

复杂度

时间复杂度为 O ( ∣ S ∣ 2 ) O(|S|^2) O(S2),空间复杂度为 O ( 1 ) O(1) O(1)

代码实现

// Problem: A. Strong Password
// Contest: Codeforces - Educational Codeforces Round 168 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1997/problem/A
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

#define int long long

// 计算字符串x的输入时间
int cal(string x)
{
    int ans = 2;
    for (int i = 1; i < x.size(); i++) {
        ans += x[i] == x[i - 1] ? 1 : 2;
    }
    return ans;
}

void solve()
{
    string s;
    cin >> s;

    int v = 0;
    string ans;
    for (int i = 0; i < s.size(); i++) {
        for (int j = 'a'; j <= 'z'; j++) {
            string t = s.substr(0, i) + char(j) + s.substr(i);
            if (cal(t) > v) {
                v = cal(t);
                ans = t;
            }
        }
    }
    for (int i = 'a'; i <= 'z'; i++) {
        string t = s + char(i);
        if (cal(t) > v) {
            v = cal(t);
            ans = t;
        }
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
}

B. Make Three Regions

题意

给一个 2 ∗ n 2*n 2n 的网格,每个单元格的颜色有两种可能(黑色 ′ x ′ 'x' x 或 白色 ′ . ′ '.' .)。

问网格中有多少个白色单元格,满足将该单元格变成黑色之后,网格中的白色连通块恰好为 3 3 3 个?

数据范围

1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105

思路

题目保证白色连通块只有一个。

若白色单元格变色后,白色连通块的数量增加二,意味着该白色单元格变色后,会与三个连通块相邻。

可以发现只有如下两种情况的白色单元格是满足条件的,统计一下即可。

1.
...    .x.
x.x -> x.x
2.
x.x    x.x
... -> .x.

复杂度

时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)

代码实现

// Problem: B. Make Three Regions
// Contest: Codeforces - Educational Codeforces Round 168 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1997/problem/B
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2e5 + 5;

int n;
string g[2];
int st[2][N];

void solve()
{
    cin >> n;
    for (int i = 0; i < 2; i++) {
        cin >> g[i];
        g[i] = 'x' + g[i] + 'x';
        for (int j = 0; j <= n; j++) {
            st[i][j] = 0;
        }
    }
    int ans = 0;
    for (int i = 0; i < 2; i++) {
        for (int j = 1; j <= n; j++) {
            if (g[i][j] == '.') {
                int add = 0;
                add += g[i ^ 1][j] == '.' && g[i][j - 1] == '.' && g[i ^ 1][j - 1] == 'x';
                add += g[i ^ 1][j] == '.' && g[i][j + 1] == '.' && g[i ^ 1][j + 1] == 'x';
                ans += add == 2;
            }
        }
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
}

C. Even Positions

思路

贪心的让右括号尽可能和最近的左括号匹配即可。

从左往右遍历字符串:
1,当前遍历到左括号 ′ ( ′ '(' (,就把该括号的下标放入未匹配的左括号下标集合 p q 1 pq_1 pq1 里面。
2,当前遍历到横线 KaTeX parse error: Expected group after '_' at position 2: '_̲',那么看是否有未匹配的左括号,在 p q 1 pq_1 pq1 中取最大下标的左括号进行匹配,否则把该横线下标放入未匹配的横线下标集合 p q 2 pq_2 pq2 里面。
3,当前遍历到右括号 ′ ) ′ ')' ),在两个集合中取最大下标进行匹配。

涉及到取最值,这两个下标集合可以用优先队列维护。因为遍历时下标是递增的,也可以用顺序维护,下标最大值必然在末尾处。

复杂度

时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)(用优先队列维护)
空间复杂度 O ( n ) O(n) O(n)

代码实现

// Problem: C. Even Positions
// Contest: Codeforces - Educational Codeforces Round 168 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1997/problem/C
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

#define int long long

int n;
string s;

void solve()
{
    cin >> n >> s;

    int ans = 0;
    priority_queue<int> pq1, pq2;
    for (int i = 0; i < n; i++) {
        char c = s[i];
        if (c == '_') {
            if (pq1.size()) {
                ans += i - pq1.top();
                pq1.pop();
            } else {
                pq2.push(i);
            }
        } else if (c == '(') {
            pq1.push(i);
        } else {
            if (pq1.size() && (!pq2.size() || pq1.top() > pq2.top())) {
                ans += i - pq1.top();
                pq1.pop();
            } else {
                ans += i - pq2.top();
                pq2.pop();
            }
        }
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
}

D. Maximize the Root

思路

因为每次操作根节点,都会使得子树内除根以外的所有节点数值减一,所以根节点的最多操作次数或者说最大增量,即为子树内除根以外节点的数值最小值。

f u f_u fu 为进行若干次操作,以 u u u 为根的子树内最大化的最小数值。

进行若干次操作后,以 u u u 的子节点 v v v 的子树内最大化的最小值 f v f_v fv,令 u u u 所有子节点 v v v 的子树内最小的最小值为 m i n ( f v ) min(f_v) min(fv)

1,若 a u ≤ m i n ( f v ) a_u \le min(f_v) aumin(fv),那么 a u a_u au 此时为 u u u 子树内的最小值,最多经过 ⌊ m i n ( f v ) − a u 2 ⌋ \lfloor \frac{min(f_v)-a_u}{2} \rfloor 2min(fv)au 次操作,使得 a u a_u au 变成 ⌊ m i n ( f v ) + a u 2 ⌋ \lfloor \frac{min(f_v)+a_u}{2} \rfloor 2min(fv)+au m i n ( f v ) min(f_v) min(fv) 变成 ⌈ m i n ( f v ) + a u 2 ⌉ \lceil \frac{min(f_v)+a_u}{2} \rceil 2min(fv)+au,如果再进行操作,最小值只会变小不会变大,因此这种情况下 f u = ⌊ m i n ( f v ) + a u 2 ⌋ f_u = \lfloor \frac{min(f_v)+a_u}{2} \rfloor fu=2min(fv)+au

2,若 a u > m i n ( f v ) a_u > min(f_v) au>min(fv),那么根节点处进行操作,只会让 m i n ( f v ) min(f_v) min(fv) 变得更小,所以这种情况下 f u = m i n ( f v ) f_u = min(f_v) fu=min(fv)

3,如果 u u u 为叶子节点,则只有一个值 a u a_u au,此时 f u = a u f_u = a_u fu=au

4,当 u u u 为整棵树的根节点 1 1 1 的时候,则直接让 a 1 a_1 a1 进行 m i n ( f v ) min(f_v) min(fv) 次操作变成 a 1 + m i n ( f v ) a_1 + min(f_v) a1+min(fv),这样即可让根节点的值最大化了。

复杂度

时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn),因为涉及到排序。
空间复杂度 O ( n ) O(n) O(n)

代码实现

// Problem: D. Maximize the Root
// Contest: Codeforces - Educational Codeforces Round 168 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1997/problem/D
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2e5 + 5;

int n;
int a[N];
vector<int> h[N];

int dfs(int u)
{
    // 叶子节点
    if (!h[u].size()) {
        return a[u];
    }
    vector<int> arr;
    for (int v : h[u]) {
        arr.push_back(dfs(v));
    }
    sort(arr.begin(), arr.end());
    if (u == 1) {
        return a[u] + arr[0];
    }
    // 如果 a[u] 大于子树最小值
    if (a[u] >= arr[0]) {
        return arr[0];
    } else {
        return (a[u] + arr[0]) / 2;
    }
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        h[i].clear();
    }
    for (int i = 2; i <= n; i++) {
        int j;
        cin >> j;
        h[j].push_back(i);
    }
    cout << dfs(1) << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
}

E. Level Up

思路

如果经过前 i − 1 i-1 i1 只怪物的战斗次数为 c n t cnt cnt,那么到达第 i i i 只怪物时的等级为 1 + ⌊ c n t k ⌋ 1 + \lfloor\frac{cnt}{k}\rfloor 1+kcnt

注意到 k k k 越小,那么在到达怪物 i i i 前的等级会越大,和怪物 i i i 进行的战斗的可能越小,所以对于每只怪物会存在一个关于 k k k 的界限,不小于这个界限怪物就会进行战斗,否则就不会。

令第 i i i 只怪物进行战斗的最小 k k k 值为 b i b_i bi,注意到 b i b_i bi有明显的二段性,考虑对每只怪物二分出该怪物的 b i b_i bi,在二分时, k k k 是确定的,但是确定与前 i − 1 i-1 i1 只怪物的战斗次数 c n t cnt cnt,如果直接进行遍历,遍历的复杂度是 O ( i ) O(i) O(i) 的,要确定 n n n 只怪物,总体的复杂度是 O ( n 2 ) O(n^2) O(n2),会超时,所以要考虑怎么优化。

注意到在前 i − 1 i-1 i1 只怪物中,如果 b j ( 1 ≤ j ≤ i − 1 ) b_j(1 \le j \le i-1) bj(1ji1) 是不小于当前二分的 k k k 值,那么第 j j j 只怪物就会战斗,问题就转换为在前 i − 1 i-1 i1 个怪物中有多少个 b j b_j bj 不小于当前二分的 k k k 值。

考虑用权值树状数组维护前 i − 1 i-1 i1 个怪物的 b j b_j bj 值,具体做法是从左到右计算怪物战斗的最小 k k k b i b_i bi,每计算出一个 b i b_i bi,则令区间 [ b i , n ] [b_i,n] [bi,n] 的数量都加一,表示之后的怪物如果取到 k k k 值不小于 b i b_i bi 的情况,第 i i i 只怪物会使得到达它们时的战斗次数加一,本质上就是区间加和单点查询的操作。

复杂度

时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn),空间复杂度 O ( n ) O(n) O(n)

代码实现

// Problem: E. Level Up
// Contest: Codeforces - Educational Codeforces Round 168 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1997/problem/E
// Memory Limit: 512 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

#define int long long

const int N = 2e5 + 5;

int n, m;
int a[N], b[N];

int tr[N];

int query(int p)
{
    int res = 0;
    while (p > 0) {
        res += tr[p];
        p -= p & (-p);
    }
    return res;
}

void modify(int p, int x)
{
    while (p <= n + 1) {
        tr[p] += x;
        p += p & (-p);
    }
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n + 2; i++) {
        tr[i] = 0;
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i];

        int l = 1, r = n + 1;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (query(mid) / mid + 1 <= a[i])
                r = mid;
            else
                l = mid + 1;
        }
        b[i] = r;
        // 二分最小进行战斗的k值
        modify(b[i], 1);
    }
    while (m--) {
        int p, x;
        cin >> p >> x;
        cout << (x >= b[p] ? "YES" : "NO") << '\n';
    }
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T = 1;
    // cin >> T;
    while (T--) {
        solve();
    }
}
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值