Contestant~ Rank 1115. Rating +1.

又是一个疯狂叉人场

A. Not a Substring

题意

给定一个长为 \(n\)不一定合法的 括号字符串 \(S\),构造一个长为 \(2n\)合法 括号字符串 \(T\),满足 \(S\) 不是 \(T\) 的子串。

思路

可以发现,只要 \(S\) 中存在连续的 \(((\) 或者 \())\),我们就只需构造 \(()()()\ldots\)

如果 \(S\) 不满足上述的条件,那么我们就构造存在连续 \(((\) 或者 \())\)\(T\),即 \((((\ldots)))\ldots\)

显然,只有 \(()\) 无法构造。

上述操作也可以更改为:

  1. 特判 \(()\)

  2. 构造 \((((\ldots)))\ldots\)

  3. 判断是否为子串;

  4. 是则输出,否则改为 \(()()()\ldots\)

至于判断子串,直接判断 \(S\) 是否是由连续的 \((((\ldots\) 和连续的 \()))\ldots\) 拼接而成即可。

当然,也可以直接无脑 \(\mathtt{KMP}\),复杂度可行。

时间复杂度:\(O(n)\)

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    string s;
    cin >> s;
    int n;
    n = s.size();
    if(s == "()"){
        cout << "NO\n";
    }else{
        cout << "YES\n";
        bool f = false;
        int ind = 0;
        for(;ind<n;ind++){
            if(s[ind] == ')'){
                break;
            }
        }
        for(;ind<n;ind++){
            if(s[ind] == '('){
                f = true;
                break;
            }
        }
        if(f){
            for(int i=0;i<n;i++) cout << "(";
            for(int i=0;i<n;i++) cout << ")";
            cout << '\n';
        }else{
            for(int i=0;i<n;i++) cout << "()";
            cout << '\n';
        }
    }
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

能让人想到 \(\mathtt{KMP}\) 还是很逆天的

B. Fancy Coins

题意

给定总额 \(m\) 元,购买下面所属的硬币,使 花哨硬币 花费数量尽可能少,并满足最后没有钱剩余:

  1. 花费 \(1\) 元的硬币:\(a_1\) 个常规硬币,无限多个花哨硬币;
  2. 花费 \(k\) 元的硬币:\(a_k\) 个常规硬币,无限多个花哨硬币

输出最少的数量。

思路

首先,既然要尽量不使用花哨硬币,我们自然会尽可能多使用 \(k\) 元的硬币。

因此,我们可以先算出全都使用 \(k\) 后的代价,然后将部分硬币换为用使用 \(1\),从而得到答案。

显然我们不可以暴力搜索,但上述可以进行分类讨论推出式子。

  1. \(a_0 + k \times a_k \geq m\),此时我们可以发现,花费 \(k\) 元的常规硬币一定是够用的,而因为需要最后没有钱剩余,我们就需要比较我们还需多少个 \(1\) 元花哨硬币,而不是输出 \(0\)

  2. \(a_0 + k \times a_k < m\),此时我们可以发现,我们还需使用 \(d = m - (a_0 + k \times a_k)\) 个硬币,那么我们尽可能多使用 \(k\),剩余的用 \(1\),然后和情况 \(1\) 一样,判断一下即可

这题也可以用三分搜索

时间复杂度:\(O(1)\)

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int m, k, a0, ak;
    cin >> m >> k >> a0 >> ak;
    int sum = a0 + k * ak;
    if (m <= sum) {
        cout << max(0ll, max(m - k * ak, m % k) - a0) << '\n';
        return;
    }
    int d = m - sum, w = d / k + d % k;
    int t = d % k == 0 ? 0 : k - d % k;
    if (a0 >= t) w = min(w, d / k + 1);
    cout << w << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

分讨分讨

C. Game on Permutation

题意

给定一个排列 \(p\) 以及一个标签,定义 \(A, B\) 之间的游戏为:

  1. 当前标签位于 \(i\)

  2. 选定一个下标 \(j\),满足 \(j < i, p_j < p_i\)

  3. 将标签移动到 \(j\)

  4. 不能移动 的一方

现在,由 \(A\) 决定标签的初始位置,然后由 \(B\) 先手开始游戏。

如果 \(A\) 决定 \(i\) 为标签的初始位置,并满足它有必胜策略,那么该点 \(i\)幸运点

输出 幸运点 的个数。

思路

首先,形象地说,对于一个点 \(i\),必胜的条件就是以 \(i\) 结尾的 最长 不一定连续 递增 子序列 的长度为 \(2\)

上述条件可以转化为:当前位置 \(i\),对应的值 \(p_i\) 满足:

  1. 大于 \(p_{[1, i - 1]}, n + 1\) 中的最小值;

  2. 小于 \(p_{[2, i - 1]}, n\) 中的最小值 \(+ 1\)

  3. \(p_i \neq 1\)

可以使用数据结构维护,也可以直接用两个变量记录。

时间复杂度:\(O(n \log n)\)

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define fs first
#define sc second
#define pb emplace_back
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for(int i=1;i<=n;i++) cin >> a[i];
    set<int> st;
    st.emplace(n + 1);
    int ans = 0;
    int mn = n + 1;
    for(int i=1;i<=n;i++){
        if(*st.begin() < a[i]){
            if(mn > a[i] && a[i] != 1) ans ++;
            mn = min(mn, a[i] + 1);
        }
        st.emplace(a[i]);
    }
    cout << ans << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
    cin >> t;
    while (t--) solve();
}

丢两个线段树上去是最抽象的

D. Balanced String

题意

给定一个二进制字符串 \(s\),定义一次操作为选定 \(i, j, i < j\) 并交换 \(s_i, s_j\)

输出让字符串中所有长为 \(2\)不一定连续 子序列中 \(01, 10\) 个数相等所需的最小操作数。

思路

首先,如果你想到了类似贪心的思路,请使用下面的强样例验证你的代码:

in: 0011110101001010000000000111100101011110100111001011000101111111010011001000110010110110001101110111
Out: 2

上述样例几乎叉掉了所有的贪心写法。

既然不可以用贪心,我们自然考虑 \(dp\)

定义 \(dp[i][j][k]\) 为 前 \(i\) 个数中包含 \(j\)\(0\)\(k\)\(01\) 串所需 修改 的最小数量,那么状态很好推:

  1. \(i\) 位置放上 \(1\),那么会多出 \(j\)\(01\) 串,此时 \(dp[i + 1][j][k + j] = dp[i][j][k] + p\)

  2. \(i\) 位置放上 \(0\),那么会多出 \(1\)\(0\),此时 \(dp[i + 1][j + 1][k] = dp[i][j][k] + p\)

上述的 \(p\) 取决于放上的数和原来位置的数是否相等,相等为 \(0\),不等为 \(1\)

那么,最后 \(dp[n][cnt0][cnt0 \times (n - cnt0)]\) 就是最少修改次数。

至于最后的答案,我们注意一下边界情况的赋值,那么就可以满足修改是成对的,最后答案就只需 \(÷2\)

时间复杂度:\(O(n ^ 4)\)

对应AC代码

#define chatgpt "bits/stdc++.h"

#include chatgpt

using namespace std;

//#define FLOATING_OCEAN

#define int long long
#define pii pair<int, int>
#define pipi pair<pii, pii>
#define tpi tuple<int, int, int>
#define pb emplace_back
#define ep emplace
#define rall(x) x.rbegin(),x.rend()
#define all(x) x.begin(),x.end()

const int N = 1e5 + 10, M = 2e5 + 10, mod = 998244353, inf = 0x3f3f3f3f3f3f3f3f;

void solve() {
    string s;
    cin >> s;
    int n = s.size();
    s = " " + s;
    vector<vector<vector<int>>> dp(2, vector<vector<int>>(n + 1, vector<int>(n * n * 2 + 1)));
    int cnt0 = 0;
    for(int i=1;i<=n;i++){
        if(s[i] == '0') cnt0 ++;
        for(int j=0;j<i+1;j++){
            for(int p=0;p<=j * (i + 1 - j);p++){
                dp[1][j][p] = n;
            }
        }
        for(int j=0;j<i;j++){
            for(int p=0;p<=j * (i - j);p++){
                dp[1][j + 1][p] = min(dp[1][j + 1][p], dp[0][j][p] + (s[i] == '1'));
                dp[1][j][p + j] = min(dp[1][j][p + j], dp[0][j][p] + (s[i] == '0'));
            }
        }
        swap(dp[0], dp[1]);
    }
    cout << dp[0][cnt0][cnt0 * (n - cnt0) / 2] / 2 << '\n';
}

signed main() {
# ifdef FLOATING_OCEAN
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
# endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
//    init();
    int t = 1;
//    cin >> t;
    while (t--) solve();
}

叉掉这么多人,直接从 \(-10\) 变成 \(+1\)