AtCoder Regular Contest 170 A-C

A - Yet Another AB Problem

贪心

定义下标\(i\)满足\(S[i]=B,T[i]=A\)\(BA\)型,\(S[i]=B,T[i]=A\)\(AB\)型,\(AA\)型、\(BB\)型同理。

对所有\(BA\)型的下标\(i\)去匹配其右侧的第一个\(AB\)型的下标\(j\),匹配成功则对下标\(i\)\(j\)进行操作,若无法匹配,则对剩余的\(BA\)型下标\(i\)去匹配其右侧的\(BB\)型下标\(j\)和对剩余的\(AB\)型下标\(i\)去匹配其左侧的\(AA\)型下标\(j\),匹配成功则对下标\(i\)\(j\)进行操作,若无法匹配则输出\(-1\),能完成匹配则输出操作数。

时间复杂度:\(O(nlogn)\),用\(vector\)替代\(set\)可优化至\(O(n)\)

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)

int main()
{
    cctie;
    
    int n; cin >> n;
    string S, T; cin >> S >> T;
    set<int> atob, btoa;
    for (int i = 0; i < n; i++) 
    {
        if (S[i] == T[i]) continue;
        if (S[i] == 'B' && T[i] == 'A') btoa.insert(i);
        else atob.insert(i);
    }
    int ans = 0;
    while (!btoa.empty() && !atob.empty() && *btoa.begin() < *(--atob.end()))
    {
        int k = *btoa.begin();
        auto ita = atob.upper_bound(k);
        int t = *ita;
        S[k] = 'A', S[t] = 'B';
        btoa.erase(k);
        atob.erase(t);
        ans++;
    }
    if (!atob.empty())
    {
        int k = n;
        for (int i = 0; i < n; i++)
        {
            if (S[i] == T[i] && S[i] == 'A')
            {
                k = i;
                break;
            }
        }
        if (k < *atob.begin()) ans += atob.size();
        else ans = -1;
    }

    if (ans != -1 && !btoa.empty())
    {
        int k = -1;
        for (int i = n - 1; i >= 0; i--)
        {
            if (S[i] == T[i] && S[i] == 'B')
            {
                k = i;
                break;
            }
        }
        if (k > *(--btoa.end())) ans += btoa.size();
        else ans = -1;
    }

    cout << ans << '\n';
    
    return 0;
}

B - Arithmetic Progression Subsequence

枚举、二分

记符合题目要求的区间为符合区间。

\([l,r]\)为符合区间时,所有区间\([x,y],1\leq x\leq l,r\leq y \leq n\)必然也是符合区间,考虑对每一个下标\(i,1\leq i\leq n\)求出所有以\(i\)为左端点的符合区间的最小右端点\(r\),则以\(i\)为左端点的非符合区间数为\(r[i]-i\)。由于数组中每个数\(\in[1,10]\),可以枚举所有三元组\(i,j,p\),满足相邻两数差值相等即\(j-i=p-j=k\),然后用二分更新所有对应值为\(i\)的下标的最小右端点\(r\)。答案即为\(\frac{(n+1)n}{2}-\sum_{i=1}^n(r[i]-i)\)

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

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define i64 long long 
#define all(x) x.begin(), x.end()

int main()
{
    cctie;
    
    int n; cin >> n;
    vector<int> b(n + 1);
    vector a(11, vector<int>(0));
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
        a[b[i]].push_back(i);
    }
    vector<int> r(n + 2, n + 1);
    for (int k = 1; k <= 4; k++)
    {
        for (int i = 1; i + 2 * k <= 10; i++)
        {
            int j = i + k;
            int p = j + k;
            for (auto ii : a[i])
            {
                auto jj = upper_bound(all(a[j]), ii);
                if (jj == a[j].end()) break;
                auto pp = upper_bound(all(a[p]), *jj);
                if (pp == a[p].end()) break;
                r[ii] = min(r[ii], *pp);
            }
            for (auto pp : a[p])
            {
                auto jj = upper_bound(all(a[j]), pp);
                if (jj == a[j].end()) break;
                auto ii = upper_bound(all(a[i]), *jj);
                if (ii == a[i].end()) break;
                r[pp] = min(r[pp], *ii);
            }
        }
    }
    for (int i = 1; i <= 10; i++)
        for (int j = 2; j < a[i].size(); j++)
            r[a[i][j - 2]] = min(r[a[i][j - 2]], a[i][j]);
    i64 ans = 1LL * (1 + n) * n / 2;
    for (int i = n; i >= 1; i--) 
    {
        r[i] = min(r[i], r[i + 1]);
        ans -= r[i] - i;
    }
    cout << ans << '\n';

    return 0;
}

C - Prefix Mex Sequence

动态规划

记符合题目要求的序列为符合序列。

\(dp[i][j]\)表示由前\(i\)个数组成的序列中值的种类数为\(j\)的符合序列数。

\(S[i]=1\)时,第\(i\)个数只能选择前\(i-1\)个数的\(mex\),为新的数,种类数加一;

\(S[i]=0\)时,第\(i\)个数可以选择除了前\(i-1\)个数的\(mex\)的所有\(\in[1, m]\)的数,若选择前\(i-1\)个数中出现过的数,则有\(j\)种选择,若选择剩余的数,则有\(m+1-j\)种选择。

状态转移方程如下:

\(S[i]=1\),则\(dp[i][j] = dp[i - 1][j - 1]\)

\(S[i]=0\),则\(dp[i][j] = dp[i - 1][j] * j + dp[i - 1][j - 1] * (m + 1 - j)\)

可以合成一个方程:\(dp[i][j] = (1 - S[i]) * dp[i - 1][j] * j + dp[i - 1][j - 1] * (1 + (1 - S[i]) * (m - j))\)

答案即为\(\sum_{j=1}^{\min(m+1,n)}dp[n][j]\)

时间复杂度:\(O(n\min(n,m))\)

#include <bits/stdc++.h>

using namespace std;

#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define i64 long long 

const int N = 5010, mod = 998244353;

int n, m;
int S[N];
i64 dp[N][N];

int main()
{
    cctie;
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> S[i];
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= min(m + 1, i); j++)
            dp[i][j] = ((1 - S[i]) * dp[i - 1][j] * j + dp[i - 1][j - 1] * (1 + (1 - S[i]) * (m - j))) % mod;
    i64 ans = 0;
    for (int j = 1; j <= min(m + 1, n); j++) ans += dp[n][j];
    cout << ans % mod << '\n';

    return 0;
}
posted @ 2024-01-22 16:09  会有续命晴空  阅读(38)  评论(0编辑  收藏  举报