AtCoder Regular Contest 170 A~D

文章介绍了两个编程题目,一个是关于字符串S和T之间的转换操作,通过贪心策略找到最少操作次数;另一个涉及计算满足特定条件的区间数量,通过动态规划求解。最后,还有一个关于博弈问题的分析,判断是否存在使Bob获胜的数字。

A.Yet Another AB Problem(贪心)

题意:

给定两个字符串SSSTTT,可以对SSS进行如下操作:选择两个字符,将前面的改为AAA,后面的改为BBB,询问至少需要几次可以将SSS变为TTT,如果不能,输出−1-11

分析:

分类讨论,如果Si=AS_i=ASi=ATi=BT_i=BTi=B,在TTT字符串中iii位置前一定要有Tj=AT_j=ATj=A,否则无法修改。如果Si=BS_i=BSi=BTi=AT_i=ATi=A,在TTT字符串中iii位置后一定要有Tj=BT_j=BTj=B,否则无法修改,如果SSS之后存在Sj=AS_j=ASj=ATj=BT_j=BTj=B,可以做到一次修改完成两次对应。贪心进行修改即可。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 5;
int sum[maxn];

int main() {
    int n;
    string s, t;
    cin >> n >> s >> t;
    queue<int> q;
    for (int i = 0; i < n; i++) {
        if (t[i] == 'B' and s[i] == 'A')
            q.push(i);
    }
    for (int i = n - 1; i >= 0; i--) {
        if (t[i] == 'B')
            sum[i]++;
        if (i >= 1)
            sum[i - 1] = sum[i];
    }
    int num = 0, ans = 0, flag = 0;
    for (int i = 0; i < n; i++) {
        if (t[i] == 'A')
            num++;
        if (s[i] != t[i]) {
            if (s[i] == 'A') {
                if (!num) {
                    cout << -1 << endl;
                    flag = 1;
                    break;
                }
                ans++;
            } else {
                if (!sum[i + 1]) {
                    cout << -1 << endl;
                    flag = 1;
                    break;
                }
                ans++;
                while (!q.empty() && q.front() < i)
                    q.pop();
                if (!q.empty()) {
                    s[q.front()] = 'B';
                    q.pop();
                }
            }
        }
    }
    if (!flag)
        cout << ans << endl;
    return 0;
}

B.Arithmetic Progression Subsequence(思维)

题意:

给出一个长度为nnn的序列aaaaia_iai1−101-10110的任意数字,一个区间[l,r][l,r][l,r]被认为是好区间当且仅当满足以下条件:

  • 存在三元组(i,j,k)(i,j,k)(i,j,k) (l≤i<j<k≤r)(l \le i < j< k \le r)(li<j<kr),满足aj−ai=ak−aja_j-a_i=a_k-a_jajai=akaj

询问好区间的数量。

分析:

对于一个固定的左端点iii,如果kkk越小,包含这个三元组的区间将会越多。遍历每一个aia_iai,枚举aja_jaj的值,再通过差值找出符合条件的最小的kkkkkk可以通过维护next1[i][j]next1[i][j]next1[i][j] 即下标jjj之后第一个数字iii的位置求得。将每一个iii所对应的最小kkk存入rir_iri,相加即可。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int a[N], next1[13][N], r[N];
vector<int> pos[13];
vector<array<int, 3>> num;

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        pos[a[i]].push_back(i);
    }
    for (int d = 0; d <= 10; ++d) {
        for (int i = 1; i + 2 * d <= 10; ++i) {
            num.push_back({i, i + d, i + 2 * d});
            if (d)
                num.push_back({i + 2 * d, i + d, i});
        }
    }
    for (int i = 1; i <= 10; ++i) {
        next1[i][n + 1] = n + 1;
        for (int j = n; j >= 1; --j) {
            if (a[j + 1] == i)
                next1[i][j] = j + 1;
            else
                next1[i][j] = next1[i][j + 1];
        }
    }
    for (int i = 1; i <= n; ++i)
        r[i] = n + 1;
    for (auto &[x, y, z]: num) {
        for (auto p: pos[x]) {
            int id = p;
            id = next1[y][id];
            id = next1[z][id];
            if (id <= n)
                r[p] = min(r[p], id);
        }
    }
    for (int i = n - 1; i >= 1; --i)
        r[i] = min(r[i], r[i + 1]);
    LL ans = 0;
    for (int i = 1; i <= n; ++i)
        ans += n - r[i] + 1;
    cout << ans << endl;
    return 0;
}

C.Prefix Mex Sequence(dp)

题意:

给出一个长度为nnn的序列ssssi=0s_i=0si=0或者si=1s_i=1si=1
询问满足以下条件的序列aaa的数量:

  • 如果si=1,ai=mex(a1,a2…ai−1)s_i=1,a_i=mex(a_1,a_2 \dots a_{i-1})si=1,ai=mex(a1,a2ai1)
  • 如果si=0,ai≠mex(a1,a2…ai−1)s_i=0,a_i \neq mex(a_1,a_2 \dots a_{i-1})si=0,ai=mex(a1,a2ai1)

请将答案对998244353998244353998244353取模。

分析:

f[i][j]f[i][j]f[i][j]表示序列aaai−1i-1i1个元素,一共使用了jjj种不同元素的方案数。

  • si=1:f[i][j]=f[i−1][j−1]s_i=1:f[i][j]=f[i-1][j-1]si=1:f[i][j]=f[i1][j1] 填入一个新的数字。

  • si=0:s_i=0:si=0: 分两种情况:

    • 填入的这个数字在之前没有出现过,除去前缀mexmexmex不可以选,那么还剩下(m−j+1)(m-j+1)(mj+1)种数字。f[i][j]=f[i−1][j−1]×(m−j+1)f[i][j]=f[i-1][j-1] \times (m-j+1)f[i][j]=f[i1][j1]×(mj+1)
    • 填入的数字之前出现过,f[i][j]=f[i−1][j]×jf[i][j]=f[i-1][j] \times jf[i][j]=f[i1][j]×j

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int mod = 998244353;
const int N = 5e3 + 5;
int s[N];
LL f[N][N];

int main() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> s[i];
    int k = min(n, m + 1);
    f[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        if (s[i]) {
            for (int j = 1; j <= k; ++j) {
                f[i][j] = f[i - 1][j - 1];
            }
        } else {
            for (int j = 0; j <= k; ++j) {
                if (j)
                    f[i][j] = (f[i][j] + (m - j + 1) * f[i - 1][j - 1]) % mod;
                f[i][j] = (f[i][j] + j * f[i - 1][j]) % mod;
            }
        }
    }
    LL ans = 0;
    for (int i = 0; i <= k; ++i)
        ans = (ans + f[n][i]) % mod;
    cout << ans << endl;
    return 0;
}

D Triangle Card Game(博弈)

题意:

给出两个数组AAABBB,询问BBB数组中是否存在一个数字使得无论从AAA数组中取出哪两个数字都无法组成三角形,如果存在这样的数字,Bob获胜,否则Alice获胜。

分析:

先将AAABBB从大到小排序,设AAA数组中取出的第一个数字为xxx,第二个数字为zzzBBB数组中取出的数字为yyy

y<xy < xy<x时,组成三角形的条件为y+z>xy+z>xy+z>x或者x+y>zx+y>zx+y>z,对于Bob来说,选择最小的yyy更不容易组成三角形。所以Bob会选择b1b_1b1

x≤yx \le yxy时,组成三角形的条件为x+y>zx+y>zx+y>z或者x+z>yx+z>yx+z>y,那么组不成三角形的条件变为min∣y−z∣≥xmin \vert y-z \vert \ge xminyzx,利用双指针进行维护。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int mod = 998244353;
const int N = 2e5 + 5;
int a[N], b[N], v[N];

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i <= n; i++)
            cin >> b[i];
        a[0] = -1e9, a[n + 1] = 2e9;
        for (int i = 0; i <= n; i++)
            v[i] = 1;
        sort(a + 1, a + 1 + n);
        sort(b + 1, b + 1 + n);
        for (int i = 1; i <= n; i++) {
            if (b[1] <= a[i]) {
                if (b[1] + a[i - 1] <= a[i] && b[1] + a[i] <= a[i + 1])
                    v[i] = 0;
            }
        }
        int num = 0, ans = 0;
        for (int i = n, j = n; i; i--) {
            if (num >= a[i])
                v[i] = 0;
            while (j && b[j] >= a[i]) {
                if (a[i - 1] + a[i] <= b[j] && a[i] + b[j] <= a[i + 1])
                    v[i] = 0;
                num = max(num, min(b[j] - a[i], a[i + 1] - b[j]));
                j--;
            }
            ans |= v[i];
        }
        if (ans)
            cout << "Alice" << endl;
        else
            cout << "Bob" << endl;
    }
    return 0;
}

学习交流


以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值