Codeforces Round 930 (Div. 2)

Codeforces Round 930 (Div. 2)

A - Shuffle Party

解题思路:

\(1\)会按着\(2\)的整数次幂往后跳。

代码:

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

void solve()
{
    int n;
    cin >> n;
    ll cur = 1;
    while (cur * 2 <= n)
    {
        cur *= 2;
    }
    cout << cur << endl;
}

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

B - Binary Path

解题思路:

最多有\(n\)种相同的解。

串的区别在于何时从第一行走向第二行。

如果右侧字符小于下面字符,一定向右走,这个位置记为\(l\)。如果右侧字符大于下面字符,此时一定得向下走,这个位置记为\(r\)

我们在\([l + 1, r]\)之间随时都可以向下走,得到的都是最小字典序字符串,\(ans = r - (l + 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 i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n;
    cin >> n;
    string a, b;
    cin >> a >> b;
    a = ' ' + a;
    b = ' ' + b;
    string ans;
    ll cnt = 0;
    int idx = n;
    int last = 0;
    for (int i = 1; i < n; i++)
    {
        if (b[i] < a[i + 1])
        {
            idx = i;
            break;
        }
        if (b[i] > a[i + 1])
        {
            last = i;
        }
    }
    if (idx == n && last == 0)
    {
        ans = a + b.back();
        cout << ans << endl;
        cout << n << endl;
    }
    else
    {
        ans = a.substr(1, idx) + b.substr(idx);
        cout << ans << endl;
        if (last == 0)
        {
            cout << idx << endl;
        }
        else
        {
            cout << idx - last << endl;
        }
    }
}

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

C - Bitwise Operation Wizard

解题思路:

首先,\(a_i |a _i = a_i\),我们可以询问\(n - 1\)次找到\(n - 1\)的下标\(pos\)

然后,找出按位或\(a_{pos}\)得到最大值的数,取这些数中的最小值位置\(j\)。因为最小值二进制中\(1\)的位置一定刚好填补\(a_{pos}\)\(0\)的位置。

按位异或最大值两位置即为\((pos, j)\)

代码:

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

int lowbit(int x)
{
    return x & -x;
}

void ask(int a, int b, int c, int d)
{
    cout << '?' << ' ' << a << ' ' << b << ' ' << c << ' ' << d << "\n";
    cout.flush();
}

void solve()
{
    int n;
    cin >> n;
    int a = 0;
    int b = 0;
    int c = 1;
    int d = 1;
    int mx = 0;
    for (int i = 1; i < n; i++)
    {
        c = d = i;
        ask(a, b, c, d);
        char ch;
        cin >> ch;
        if (ch == '<')
        {
            a = b = c;
        }
    }
    mx = a;
    int cur = 0;
    for (int i = 1; i < n; i++)
    {
        ask(mx, cur, mx, i);
        char ch;
        cin >> ch;
        if (ch == '<')
        {
            cur = i;
        }
        else if (ch == '=')
        {
            ask(cur, cur, i, i);
            cin >> ch;
            if (ch == '>')
            {
                cur = i;
            }
        }
    }
    cout << '!' << ' ' << mx << ' ' << cur << endl;
}

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

D - Pinball

解题思路:

对案例模拟一下可以发现,对于位置\(i\),最终是从左出去还是从右边出去取决于,自身的符号,左侧\(>\)的数量以及,右侧\(<\)的数量。

举例:\((1, 2, 3, 4, 5):>><<<\)

对于位置\(3\)而言,(左侧\(>\)数量) 小于等于 (右侧\(<\)数量),最终从左边出去。

对于答案的计算,下标设位置分别为\((p_1,p_2,i, p_3, p_5)\):

\[\begin{align*} &(i - p_2 + 1) + (p_3 - p_2) + (p_3 - p_1) + (p_4 - p_1) + (n - p_1) \\ = &2 \times[(p_3 + p_4) - (p_1 + _2)] + n + i + 1 \end{align*} \]

根据公式,不难发现,前后缀规律,具体位置可用二分查找到,其余情况也这样讨论即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
typedef double db;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = ' ' + s;
    // prel[i]: i左边 < 的数量
    // prer[i]: i左边 > 的数量
    vector<int> prel(n + 1), prer(n + 1);
    for (int i = 1; i <= n; i++)
    {
        prel[i] = prel[i - 1];
        prer[i] = prer[i - 1];
        if (s[i] == '<')
        {
            prel[i]++;
        }
        else
        {
            prer[i]++;
        }
    }
    vector<ll> pre(n + 10), suf(n + 10);
    for (int i = 1; i <= n; i++)
    {
        pre[i] = pre[i - 1];
        suf[i] = suf[i - 1];
        if (s[i] == '>')
        {
            pre[i] += i;
        }
        else
        {
            suf[i] += i;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        int lc = prer[i - 1];
        int rc = prel[n] - prel[i];
        if (s[i] == '<')
        {
            // 最终从左边出去
            if (lc <= rc)
            {
                if (lc == 0)
                {
                    cout << i << ' ';
                    continue;
                }
                else
                {
                    int l = i;
                    int r = n + 1;
                    while (l + 1 < r)
                    {
                        int mid = l + r >> 1;
                        if (prel[mid] - prel[i] >= lc)
                        {
                            r = mid;
                        }
                        else
                        {
                            l = mid;
                        }
                    }
                    ll ans = 2 * (suf[r] - suf[i] - (pre[i - 1])) + i;
                    cout << ans << ' ';
                }
            }
            // 最终从右边出去
            else
            {
                int l = 0;
                int r = i;
                while (l + 1 < r)
                {
                    int mid = l + r >> 1;
                    if (prer[i - 1] - prer[mid] >= rc + 1)
                    {
                        l = mid;
                    }
                    else
                    {
                        r = mid;
                    }
                }
                ll ans = 2 * (suf[n] - suf[i] - (pre[i - 1] - pre[l])) + n + i + 1;
                cout << ans << ' ';
            }
        }
        else
        {
            // 最终从右边出去
            if (rc <= lc)
            {
                if (rc == 0)
                {
                    cout << n - i + 1 << ' ';
                    continue;
                }
                else
                {
                    int l = 0;
                    int r = i;
                    while (l + 1 < r)
                    {
                        int mid = l + r >> 1;
                        if (prer[i - 1] - prer[mid] >= rc)
                        {
                            l = mid;
                        }
                        else
                        {
                            r = mid;
                        }
                    }
                    ll ans = 2 * (suf[n] - suf[i] - (pre[i - 1] - pre[l])) + n - i + 1;
                    cout << ans << ' ';
                }
            }
            // 最终从左边出去
            else
            {
                int l = i;
                int r = n + 1;
                while (l + 1 < r)
                {
                    int mid = l + r >> 1;
                    if (prel[mid] - prel[i] > lc)
                    {
                        r = mid;
                    }
                    else
                    {
                        l = mid;
                    }
                }
                ll ans = 2 * (suf[r] - suf[i] - (pre[i - 1])) - i;
                cout << ans << ' ';
            }
        }
    }
    cout << endl;
}

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

E - Pokémon Arena

(推荐讲解视频)

虚点优化建图,跑最短路。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
typedef double db;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const ll inf = 1ll << 60;
void solve()
{
    int n, m;
    cin >> n >> m;
    vector<ll> c(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> c[i];
    }
    vector<vector<pii>> a(m + 10, vector<pii>()), e(3 * m * n + 10, vector<pii>());
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            int x;
            cin >> x;
            a[j].emplace_back((pii){x, i});
        }
    }
    int idx = n + 1;
    // 对于每个点i,对于每个属性分别建立两个虚点
    // 一个用来增加属性值挑战强者
    // 一个用来欺负弱者
    for (int j = 1; j <= m; j++)
    {
        sort(a[j].begin(), a[j].end());
        // 对i建通往属性j值的边,数值大小不变,只出出场费c[i]
        for (int i = 0; i < n; i++)
        {
            int id = a[j][i].se;
            e[id].push_back({idx, c[id]});
            // 建一条战胜强者的边,花费就是二者属性值之差
            if (i != n - 1)
            {
                ll cost = a[j][i + 1].fi - a[j][i].fi;
                e[idx].push_back({idx + 1, cost});
            }
            // 从属性本值点,进入保守通道,该通道不在会增加属性值,只会有高属性值连向低属性值
            e[idx].push_back({idx + n, 0});
            idx++;
        }
        for (int i = 0; i < n; i++)
        {
            int id = a[j][i].se;
            // 能到达保守位,说明具备击败该点的实力,从该点的保守位去击败该点
            e[idx].push_back({id, 0});
            // 在保守区域,建一条击败弱者的边,不需要增加属性值,无多余花费
            if (i != n - 1)
            {
                e[idx + 1].push_back({idx, 0});
            }
            idx++;
        }
    }
    // 跑一条从n到1的最短路即可。
    vector<ll> dist(3 * m * n + 10, inf);
    dist[n] = 0;
    priority_queue<pii, vector<pii>, greater<pii>> q;
    q.push({dist[n], n});
    vector<bool> vis(3 * n * m + 10, false);
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        int u = t.se;
        if (vis[u])
        {
            continue;
        }
        vis[u] = true;
        for (auto x : e[u])
        {
            int v = x.fi;
            ll w = x.se;
            if (dist[v] > dist[u] + w)
            {
                dist[v] = dist[u] + w;
                q.push({dist[v], v});
            }
        }
    }
    cout << dist[1] << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
posted @ 2024-03-01 00:35  value0  阅读(623)  评论(0)    收藏  举报