Educational Codeforces Round 166 個人題解(A~D)

ExtractStars發表於2024-05-31

Educational Codeforces Round 166 (Rated for Div. 2)個人題解

題解火車頭

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <unordered_map>
#include <cstring>
#include <cstdio>
#include <string>
#include <queue>
#include <stack>
#include <map>
#include <list>
#include <bitset>
#include <cmath>
#include <numeric>

#define endl '\n'

#define ft first
#define sd second

#define yes cout << "yes\n"
#define no cout << "no\n"

#define Yes cout << "Yes\n"
#define No cout << "No\n"

#define YES cout << "YES\n"
#define NO cout << "NO\n"

#define pb push_back
#define eb emplace_back

#define all(x) x.begin(), x.end()
#define unq_all(x) x.erase(unique(all(x)), x.end())
#define sort_all(x) sort(all(x))
#define reverse_all(x) reverse(all(x))

#define INF 0x7fffffff
#define INFLL 0x7fffffffffffffffLL

#define RED cout << "\033[91m"
#define GREEN cout << "\033[92m"
#define YELLOW cout << "\033[93m"
#define BLUE cout << "\033[94m"
#define MAGENTA cout << "\033[95m"
#define CYAN cout << "\033[96m"
#define RESET cout << "\033[0m"

// 紅色
#define DEBUG1(x)                     \
    RED;                              \
    cout << #x << " : " << x << endl; \
    RESET;

// 綠色
#define DEBUG2(x)                     \
    GREEN;                            \
    cout << #x << " : " << x << endl; \
    RESET;

// 藍色
#define DEBUG3(x)                     \
    BLUE;                             \
    cout << #x << " : " << x << endl; \
    RESET;

// 品紅
#define DEBUG4(x)                     \
    MAGENTA;                          \
    cout << #x << " : " << x << endl; \
    RESET;

// 青色
#define DEBUG5(x)                     \
    CYAN;                             \
    cout << #x << " : " << x << endl; \
    RESET;

// 黃色
#define DEBUG6(x)                     \
    YELLOW;                           \
    cout << #x << " : " << x << endl; \
    RESET;

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;

typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pdd;
typedef pair<string, string> pss;
typedef pair<string, int> psi;
typedef pair<string, ll> psl;

typedef vector<bool> vb;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<string> vs;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef vector<pss> vpss;

typedef vector<vi> vvi;
typedef vector<vl> vvl;

typedef queue<int> qi;
typedef queue<ll> ql;
typedef queue<pii> qpii;
typedef queue<pll> qpll;
typedef queue<psi> qpsi;
typedef queue<psl> qpsl;

typedef priority_queue<int> pqi;
typedef priority_queue<ll> pql;
typedef priority_queue<string> pqs;
typedef priority_queue<pii> pqpii;
typedef priority_queue<psi> pqpsi;
typedef priority_queue<pll> pqpl;
typedef priority_queue<psi> pqpsl;

typedef map<int, int> mii;
typedef map<int, bool> mib;
typedef map<ll, ll> mll;
typedef map<ll, bool> mlb;
typedef map<char, int> mci;
typedef map<char, ll> mcl;
typedef map<char, bool> mcb;
typedef map<string, int> msi;
typedef map<string, ll> msl;
typedef map<int, bool> mib;

typedef unordered_map<int, int> umii;
typedef unordered_map<ll, ll> uml;
typedef unordered_map<char, int> umci;
typedef unordered_map<char, ll> umcl;
typedef unordered_map<string, int> umsi;
typedef unordered_map<string, ll> umsl;

template <typename T>
inline void read(T &x)
{
    T f = 1;
    x = 0;
    char ch = getchar();
    while (0 == isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (0 != isdigit(ch))
        x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
    x *= f;
}

template <typename T>
inline void write(T x)
{
    if (x < 0)
    {
        x = ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

Problem - 1976A - Codeforces

A. 驗證密碼

每個測試的時間限制:2秒
每個測試的記憶體限制:256兆位元組
輸入:標準輸入
輸出:標準輸出

Monocarp 正在開發他的新網站,目前面臨的挑戰是如何讓使用者選擇強密碼。

Monocarp 認為,強密碼應滿足以下條件:

  • 密碼只能由小寫拉丁字母和數字組成;
  • 字母后面不能有數字(因此,每個字母后面要麼有另一個字母,要麼字串結束);
  • 所有數字應按非遞減順序排序;
  • 所有字母應按非遞減順序排序。

請注意,密碼可以只有字母或只有數字。

Monocarp 成功地實現了第一個條件,但他在其餘條件上很吃力。您能幫他驗證密碼嗎?

輸入

第一行包含一個整數 $t$ $(1 \leq t \leq 1000)$ — 測試用例的數量。

每個測試用例的第一行包含一個整數 $n$ $(1 \leq n \leq 20)$ — 密碼的長度。

第二行包含一個字串,正好由 $n$ 個字元組成。每個字元都是小寫拉丁字母或數字。

輸出

對於每個測試用例,如果給定的密碼很強,則列印“YES”,否則列印“NO”。

示例

輸入
5
4
12ac
5
123wa
9
allllmost
5
ac123
6
011679
輸出
YES
NO
YES
NO
YES

在第二個測試用例中,字母沒有按非遞減順序排序。

在第四個測試用例中,字母后面有一個數字 —— 字母 'c' 後面是數字 '1'。

解題思路

查詢是否存在$a1$這樣前面為字母后面為數字的欄位和前面大,後面小的欄位就行

題解

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    bool flag = true;
    for (int i = 1; i < n; i++)
    {
        if (isalpha(s[i - 1]) && isdigit(s[i]))
        {
            flag = false;
            break;
        }
        if (s[i - 1] > s[i])
        {
            flag = false;
            break;
        }
    }
    if (flag)
        YES;
    else
        NO;
}

int main()
{
    ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
    int _ = 1;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

Problem - 1976B - Codeforces

B. 增加/減少/複製

每個測試的時間限制:2秒
每個測試的記憶體限制:256兆位元組
輸入:標準輸入
輸出:標準輸出

給你兩個整數陣列:長度為 $n$ 的陣列 $a$ 和長度為 $n+1$ 的陣列 $b$。

你可以按任意順序多次執行下列操作:

  • 選擇陣列 $a$ 中的任意元素,並將其增加 1;
  • 選擇陣列 $a$ 中的任意元素,並將其減少 1;
  • 從陣列 $a$ 中任意選擇一個元素,複製並追加到陣列 $a$ 的末尾。

你的任務是計算將陣列 $a$ 轉換為陣列 $b$ 所需的上述操作的最少次數(可能為零)。可以證明,在問題的限制條件下,這總是可能的。

輸入

第一行包含一個整數 $t$ $(1 \leq t \leq 10^4)$ — 測試用例的數量。

每個測試用例由三行組成:

  • 第一行包含一個整數 $n$ $(1 \leq n \leq 2 \times 10^5)$;
  • 第二行包含 $n$ 個整數 $a_1, a_2, \dots, a_n$ $(1 \leq a_i \leq 10^9)$;
  • 第三行包含 $n+1$ 個整數 $b_1, b_2, \dots, b_{n+1}$ $(1 \leq b_i \leq 10^9)$。

輸入的附加限制:所有測試用例中 $n$ 的總和不超過 $2 \times 10^5$。

輸出

對於每個測試用例,列印一個整數 — 將陣列 $a$ 轉換為陣列 $b$ 所需的最少運算元(可能為零)。

示例

輸入
3
1
2
1 3
2
3 3
3 3 3
4
4 2 1 2
2 1 5 2 3
輸出
3
1
8

在第一個示例中,可以將 $a$ 轉換為 $b$ 如下:$[2] \rightarrow [2,2] \rightarrow [1,2] \rightarrow [1,3]$。

解題思路

對於$a_i->bi,1\le i \le n$,轉變的最小代價就是它們的差值。

對於$b_{n+1}$,得到它的最小代價就是$a_i->b_i$過程中與它最小的差值$+1$。

題解

void solve()
{
    int n;
    cin >> n;
    vi a(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    vi b(n + 1);
    for (int i = 0; i < n + 1; i++)
    {
        cin >> b[i];
    }
    int need = INF;
    ll ans = 0;
    for (int i = 0; i < n; i++)
    {
        ans += abs(b[i] - a[i]);
        need = min(need, abs(b[n] - b[i]));
        need = min(need, abs(b[n] - a[i]));
        if (a[i] < b[n] && b[n] < b[i] || a[i] > b[n] && b[n] > b[i])
            need = 0;
    }
    cout << ans + need + 1 << endl;
}

int main()
{
    ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
    int _ = 1;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

Problem - 1976C - Codeforces

C. 工作面試

Monocarp 打算開一家自己的 IT 公司。他想招聘 $n$ 名程式設計師和 $m$ 名測試員。

有 $n + m + 1$ 個候選人,按到達時間順序從 1 到 $n + m + 1$ 依次編號。第 $i$ 名候選人的程式設計技能為 $a_i$,測試技能為 $b_i$(一個人的程式設計技能和測試技能是不同的)。團隊的技能是所有被聘為程式設計師的候選人的程式設計技能之和,以及所有被聘為測試員的候選人的測試技能之和。

當應聘者前來面試時,Monocarp 會嘗試將其分配到最適合的職位(如果應聘者的程式設計技能較高,則錄用其為程式設計師,否則錄用其為測試員)。如果該職位的所有名額都已招滿,Monocarp 就會將他們分配到其他職位。

你的任務是,針對每個候選人,計算如果除他們之外的所有人都來面試,團隊的技能。請注意,這意味著正好有 $n + m$ 名候選人來參加面試,因此公司的所有 $n + m$ 個職位都將被填滿。

輸入

第一行包含一個整數 $t$ $(1 \leq t \leq 10^4)$ — 測試用例的數量。

每個測試用例由三行組成:

  • 第一行包含兩個整數 $n$ 和 $m$ $(0 \leq n, m \leq 2 \cdot 10^5; 2 \leq n + m + 1 \leq 2 \cdot 10^5)$ — 分別是 Monocarp 希望僱用的程式設計師人數和測試員人數;
  • 第二行包含 $n + m + 1$ 個整數 $a_1, a_2, \dots, a_{n+m+1}$ $(1 \leq a_i \leq 10^9)$,其中 $a_i$ 是第 $i$ 個候選人的程式設計技能;
  • 第三行包含 $n + m + 1$ 個整數 $b_1, b_2, \dots, b_{n+m+1}$ $(1 \leq b_i \leq 10^9; b_i \neq a_i)$,其中 $b_i$ 是第 $i$ 個候選人的測試技能。

輸入的附加限制:所有測試用例的 $(n + m + 1)$ 之和不超過 $2 \cdot 10^5$。

輸出

對於每個測試用例,列印 $n + m + 1$ 個整數,其中第 $i$ 個整數應該等於如果除了第 $i$ 個候選人之外的所有人都來面試時團隊的技能。

示例

輸入
4
1 0
2 1
1 2
0 2
4 5 5
5 4 1
1 2
2 1 5 4
5 2 3 1
3 1
4 3 3 4 1
5 5 4 5 2
輸出
1 2 
5 6 9 
8 11 11 12 
13 13 13 12 15 

說明

讓我們來看看示例中的第三個測試案例:

  • 如果第一個候選者沒有來,第二個候選者會被聘為測試員,第三個候選者會被聘為程式設計師,第四個候選者會被聘為測試員。團隊的總技能為 $2 + 5 + 1 = 8$;
  • 如果第二個候選者沒有來,第一個候選者會被聘為測試員,第三個候選者會被聘為程式設計師,第四個候選者會被聘為測試員。團隊的總技能為 $5 + 5 + 1 = 11$;
  • 如果第三個候選者沒有來,第一個候選者會被聘為測試員,第二個候選者會被聘為測試員,第四個候選者會被聘為程式設計師。團隊的總技能為 $5 + 2 + 4 = 11$;
  • 如果第四個候選者沒有來,第一個候選者會被聘為測試員,第二個候選者會被聘為測試員,第三個候選者會被聘為程式設計師。團隊的總技能為 $5 + 2 + 5 = 12$。

解題思路

從前往後遍歷,檢查一下是$a$能力值浪費了還是$b$能力值浪費了,然後從後往前列舉,開一個陣列維護一下最近字尾損失能力值。輸出答案的時候,如果當前的人的站的職位剛好是能力值被浪費的職位,輸出總和減去當前的人的能力值加上最近損失能力值

題解

typedef struct people
{
    ll a;
    ll b;
    int job;   // 被分配到了a職位就是1,b職位就是2
    bool lose; // 是否浪費了能力
} people;

void solve()
{
    int n, m;
    cin >> n >> m;
    int N = n + m + 1;
    vector<people> p(N + 1);
    for (int i = 1; i <= N; i++)
    {
        cin >> p[i].a;
    }
    for (int i = 1; i <= N; i++)
    {
        cin >> p[i].b;
    }
    // 能力總和
    ll sum = 0;
    // 哪種能力被浪費了
    int sta = 0;
    for (int i = 1; i <= N; i++)
    {
        if (p[i].a > p[i].b)
        {
            // a職位減1
            n--;
            // a職位沒有了
            if (n < 0)
            {
                sta = 1; // 有人的a能力被浪費了
                sum += p[i].b;
                p[i].job = 2;     // 這個人被分配到了b職位
                p[i].lose = true; // 他的能力被浪費了
            }
            else
            {
                sum += p[i].a;
                p[i].job = 1;      // 這個人被分配到了a職位
                p[i].lose = false; // 他沒有能力被浪費
            }
        }
        else
        {
            // b職位減1
            m--;
            // b職位沒有了
            if (m < 0)
            {
                sta = 2; // 有人的b能力被浪費了
                sum += p[i].a;
                p[i].job = 1;     // 這個人被分配到了a職位
                p[i].lose = true; // 他的能力被浪費了
            }
            else
            {
                sum += p[i].b;
                p[i].job = 2;      // 這個人被分配到了b職位
                p[i].lose = false; // 他沒有能力被浪費
            }
        }
    }
    // 最近被浪費的能力值
    vl nearest_loss(N + 1);
    // 從後往前遍歷
    for (int i = N; i >= 1; i--)
    {
        // 如果這個人能力被浪費了,那麼最近被浪費的能力值就是他能力值和另一個能力值之差的絕對值
        if (p[i].lose)
            nearest_loss[i] = abs(p[i].a - p[i].b);
        // 如果這個人能力沒有被浪費,那麼最近被浪費的能力值就是下一個人的最近被浪費的能力值
        else
            nearest_loss[i] = nearest_loss[i + 1];
    }
    // 從前往後輸出
    for (int i = 1; i <= N; i++)
    {
        // 輸出總和減去當前的人的能力值,判斷一下他是否空出了位置給能力被浪費的人,是的話加上最近被浪費的能力值
        cout << sum - (p[i].job == 1 ? p[i].a : p[i].b) + (p[i].job == sta ? nearest_loss[i] : 0) << " ";
    }
    cout << endl;
}

int main()
{
    ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
    int _ = 1;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

Problem - 1976D - Codeforces

D. 可逆括號序列

一個正則括號序列是指可以透過在序列的原始字元之間插入字元 '1' 和 '+' 來轉換成正確算術表示式的括號序列。例如:

  • 括號序列 "()" 和 "(())" 是正則的(得到的表示式分別是:"(1)+(1)" 和 "((1+1)+1)");
  • 括號序列 ")(", "(" 和 ")" 不是正則的。

我們定義括號序列的逆序如下:用 ')' 替換所有括號 '(',反之亦然(用 '(' 替換所有括號 ')')。例如,字串 "()((" 和 ")())" 互為逆序。

給你一個正則括號序列 $s$。計算滿足以下條件的整數對 $(l, r)$ 的數量(1 ≤ l ≤ r ≤ |s|):如果將 $s$ 中從第 $l$ 個字元到第 $r$ 個字元(包含第 $l$ 和第 $r$ 個字元)的子串替換成其逆序,$s$ 仍然是一個正則括號序列。

輸入

第一行包含一個整數 $t$ $(1 \leq t \leq 10^4)$ — 測試用例的數量。

每個測試用例的唯一一行包含一個非空的正則括號序列;該序列僅由字元 '(' 和 ')' 組成。

輸入的附加限制:所有測試用例的正則括號序列總長度不超過 $2 \cdot 10^5$。

輸出

對於每個測試用例,列印一個整數 — 符合題目條件的 $(l, r)$ 對的數量。

示例

輸入
4
(())
()
()()()
(()())(())
輸出
1
0
3
13

說明

在第一個示例中,只有一對:

  • $(2, 3)$: (()) → ()().

在第二個示例中,沒有一對。

在第三個示例中,有三對:

  • $(2, 3)$: ()()() → (())().
  • $(4, 5)$: ()()() → ()(()).
  • $(2, 5)$: ()()() → (()()).

解題思路

我們將"("轉化成$+1$,將")"轉化成$-1$​​。對字串做字首和,我們就得到了一個在數軸上的折線。

選擇一個區間翻轉,實際上就是將這條折線沿著線段$l-r$翻折。

下圖是測試用例$3$的折線圖和可翻轉的點。

image-20240531024626248

不難發現,滿足題目要求的區間$[l,r]$,一定滿足$sum[l]>=0,sum[r]==sum[l-1],max(sum[l\sim r])<=2 \times sum[r]$​

從前往後列舉$sum$統計一下高度相同的點的數量並累加即可。注:如果翻轉會到達負值域的點要賦值為0。

題解

void solve()
{
    string s;
    cin >> s;
    int n = s.size();
    vi sum(n + 1);
    for (int i = 0; i < n; i++)
    {
        sum[i + 1] = sum[i] + (s[i] == '(' ? 1 : -1);
    }
    vi high(n + 1);
    high[0] = 1;
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        ans += high[sum[i]];
        high[sum[i]]++;
        if (sum[i] > 0)
            high[(sum[i] - 1) / 2] = 0;
    }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);
    int _ = 1;
    std::cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

相關文章