Educational Codeforces Round 163 (Rated for Div. 2)

value0發表於2024-03-16

Educational Codeforces Round 163 (Rated for Div. 2)

A - Special Characters

解題思路:

一個相同的連續段會貢獻兩個特殊字元,所以答案一定是偶數,找個不同的數分隔開即可。

程式碼:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;

void solve()
{
    int n;
    cin >> n;
    if (n & 1)
    {
        puts("NO");
        return;
    }
    string a = "AA";
    string b = "B";
    string ans;
    n /= 2;
    for (int i = 1; i <= n; i++)
    {
        ans += a + b;
    }
    puts("YES");
    cout << ans << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

B - Array Fix

解題思路:

從左到右找到最後一個\(a_i > a_{i + 1}\),記錄\(idx = i\)

對於\(1 \sim i\)都進行操作,然後從左到右判斷合法即可。

程式碼:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    int idx = -1;
    for (int i = 1; i <= n - 1; i++)
    {
        if (a[i] > a[i + 1])
        {
            idx = i;
        }
    }
    if (idx == -1)
    {
        puts("YES");
        return;
    }
    vector<int> ans;
    for (int i = 1; i <= idx; i++)
    {
        int x = a[i];
        vector<int> t;
        while (x)
        {
            t.push_back(x % 10);
            x /= 10;
        }
        reverse(t.begin(), t.end());
        for (auto c : t)
        {
            ans.push_back(c);
        }
        if (a[i] == 0)
        {
            ans.push_back(0);
        }
    }
    for (int i = idx + 1; i <= n; i++)
    {
        ans.push_back(a[i]);
    }
    // cout << ans[0] << endl;
    for (int i = 1; i < ans.size(); i++)
    {
        // cout << ans[i] << endl;
        if (ans[i] < ans[i - 1])
        {
            puts("NO");
            return;
        }
    }
    puts("YES");
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

C - Arrow Path

解題思路:

分類討論:

  • 右邊為\(>\):向右走兩格。
  • 右邊為\(<\):不動。
  • 下邊為\(>\):向右下走。
  • 下邊為\(<\):向左下走。
  • 左邊為\(<\):向左走兩格。嘗試舉例後發現沒必要。
  • 左邊為\(>\):不動。

\((1, 1)\)開始合法轉移即可。

程式碼:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;

void solve()
{
    int n;
    cin >> n;
    vector<string> g(3);
    vector<vector<int>> dp(6, vector<int>(n + 10, 0));
    for (int i = 1; i <= 2; i++)
    {
        cin >> g[i];
        g[i] = ' ' + g[i];
    }
    dp[1][1] = 1;
    for (int j = 1; j <= n; j++)
    {
        for (int i = 1; i <= 2; i++)
        {
            if (i == 1)
            {
                if (g[i + 1][j] == '>')
                {
                    dp[i + 1][j + 1] |= dp[i][j];
                }
                else
                {
                    dp[i + 1][j - 1] |= dp[i][j];
                }
                if (g[i][j + 1] == '>')
                {
                    dp[i][j + 2] |= dp[i][j];
                }
            }
            else
            {
                if (g[i - 1][j] == '>')
                {
                    dp[i - 1][j + 1] |= dp[i][j];
                }
                else
                {
                    dp[i - 1][j - 1] |= dp[i][j];
                }
                if (g[i][j + 1] == '>')
                {
                    dp[i][j + 2] |= dp[i][j];
                }
            }
        }
    }
    if (dp[2][n])
    {
        puts("YES");
    }
    else
    {
        puts("NO");
    }
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

D - Tandem Repeats?

解題思路:

列舉合法串的長度,然後列舉串的起點。

舉例:\(abcabda\)

假設我們一半長度為\(3\),對於\(abc,abd\)是不合法的,那麼從\(b\)開始,也就是\(bca,bda\)一定也是不合法的。

程式碼:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;
using ull = unsigned long long;

void solve()
{
    string s;
    cin >> s;
    int n = s.size();
    s = ' ' + s;
    int ans = 0;
    for (int len = n / 2; len > 0; len--)
    {
        int cnt = 0;
        for (int i = 1; i + len <= n; i++)
        {
            if (s[i] == '?' || s[i + len] == '?' || s[i] == s[i + len])
            {
                cnt++;
                if (cnt == len)
                {
                    ans = max(ans, cnt);
                    break;
                }
            }
            else
            {
                cnt = 0;
            }
        }
    }
    cout << ans * 2 << endl;
}

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

E - Clique Partition

解題思路:

因為\((i,j)\)相近的兩個數,\(abs(i - j)\)小,所以應該是一個一個連續區間中的數為一個團。

\((1, 2,3,4,5,6,7,8,9,10),k = 8\)

\(abs(1 - 9)=8,a_1 \neq a_9\),所以理論上,相隔\(8\)的兩個數不會在一個團中。同理可推廣到\(k\)

考慮能否讓\(abs(i - j) < k\)的數都在一個團中。

考慮\(abs(i - j)\)較大的點對,由於下標差已經較大,我們需要讓點權差儘量小。

對於\((1, 8)\)\(abs(1-8)=7\),所以\(a_1 - a_8 = 1\)才能使二者符合連邊條件。

嘗試後發現,最平衡的方法為

\[\begin{align*} i:\ &1,2,3,4,5,6,7,8 \\ a_i:\ &4,3, 2, 1,8,7,6,5 \end{align*} \]

所以,從左到右,每\(k\)個這樣進行構造,最後多出不足\(k\)個的,就按照剩餘個數進行構造。

程式碼:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;
using ull = unsigned long long;
const int N = 110;
int ans;
int a[N];
int c[N];

void dfs(int i, int n, int k, int id)
{
    if (i > n)
    {
        return;
    }
    ans = id;
    // 當前團中的點數
    int num = min(n - i + 1, k);
    int cur = i;
    // 1 2 3 4
    // 4 3 2 1
    for (int j = i + num / 2 - 1; j >= i; j--)
    {
        a[j] = cur++;
        c[j] = id;
    }
    // 5 6 7 8
    // 8 7 6 5
    for (int j = i + num - 1; j >= i + num / 2; j--)
    {
        a[j] = cur++;
        c[j] = id;
    }
    dfs(i + num, n, k, id + 1);
}

void solve()
{
    int n, k;
    cin >> n >> k;
    dfs(1, n, k, 1);
    for (int i = 1; i <= n; i++)
    {
        cout << a[i] << " \n"[i == n];
    }
    cout << ans << "\n";
    for (int i = 1; i <= n; i++)
    {
        cout << c[i] << " \n"[i == n];
    }
}

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

F - Rare Coins

解題思路:

區間內金幣有\(g_1\)個,銀幣有\(s_1\)個。區間外有金幣\(g_2\)個,銀幣\(s_2\)個。

我們設區間內有\(a\)個銀幣為\(1\),區間外有\(b\)個銀幣為\(0\)

\[\begin{align*} g_1 + a &> g_2 + s_2 - b\\ a + b &> g_2 + s_2 -g_1\\ a + b &\geq g_2 + s_2 - g_1 + 1 \end{align*} \]

其中,\((g_2 + s_2-g_1+1 \leq a + b\leq s1 + s2)\)

\[\sum\limits_{a + b = g_2 + s_2 - g_1 + 1}^{s1 + s2}\sum\limits_{k = 0}^{a + b}C_{s_1}^{k}C_{s_2}^{a + b - k} \]

範德蒙德卷積:

\[\sum\limits_{i=0}^{k}C_{a}^i C_b^{k - i} = C_{a + b}^{k} \]

所以:

\[\sum\limits_{a + b = g_2 + s_2 - g_1 + 1}^{s1 + s2}\sum\limits_{k = 0}^{a + b}C_{s_1}^{k}C_{s_2}^{a + b - k}=\sum\limits_{a + b = g_2 + s_2 - g_1 + 1}^{s1 + s2}C_{s_1+s_2}^{a+b} \]

我們已經求出了所有區間內價值大於區間外價值的情況,最後記得乘上\((\frac{1}{2})^{s_1 + s_2}\)的逆元。

程式碼:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;
using ull = unsigned long long;
const int mod = 998244353;
const int N = 1e6 + 10;
ll f[N];
ll inv[N];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = res * a % mod;
        }
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}

void init()
{
    const int n = 1e6;
    f[0] = inv[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        f[i] = f[i - 1] * i % mod;
    }
    inv[n] = qmi(f[n], mod - 2);
    for (int i = n - 1; i >= 1; i--)
    {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }
}

ll C(ll a, ll b)
{
    if (a < 0 || b < 0 || a < b)
        return 0;
    return (f[a] * inv[b] % mod) * (inv[a - b]) % mod;
}

void solve()
{
    int n, q;
    cin >> n >> q;
    vector<array<ll, 2>> s(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i][0];
        s[i][0] += s[i - 1][0];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> s[i][1];
        s[i][1] += s[i - 1][1];
    }
    const ll m = s[n][1];
    vector<ll> pre(m + 1);
    for (int i = 0; i <= m; i++)
    {
        pre[i] = C(m, i) % mod;
    }
    for (int i = 1; i <= m; i++)
    {
        pre[i] = (pre[i] + pre[i - 1]) % mod;
    }
    ll d = qmi(qmi(2, m), mod - 2) % mod;
    auto get = [&](ll l, ll r) -> ll
    {
        if (l > r)
        {
            return 0;
        }
        return pre[m] - (l >= 1 ? pre[l - 1] : 0) + mod;
    };
    while (q--)
    {
        int l, r;
        cin >> l >> r;
        ll g1 = s[r][0] - s[l - 1][0];
        ll s1 = s[r][1] - s[l - 1][1];
        ll g2 = s[n][0] - g1;
        ll s2 = s[n][1] - s1;
        // g1 + a > g2 + s2 - b
        // a + b >= g2 + s2 - g1 + 1
        // g2 + s2 - g1 + 1 ~ s1 + s2
        // C(s1, a) * C(s2, b) -> C(s1 + s2, a + b) (g2 + s2 - g1 + 1 <= a + b <= s1 + s2)
        // m = s1 + s2
        // cout << g2 + s2 - g1 + 1 << ' ' << m << endl;
        cout << (get(g2 + s2 - g1 + 1, m) * d) % mod << ' ';
    }
    cout << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    init();
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

相關文章