Codeforces Round 932 (Div. 2)

Codeforces Round 932 (Div. 2)

A - Entertainment in MAC

解题思路:

如果翻转字符小于原字符,那么一直翻转即可。

否则,翻转\(n - 1\)次,然后添加一次。

代码:

#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;
const int N = 6010;

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    string rs = s;
    reverse(rs.begin(), rs.end());
    if (s <= rs)
    {
        cout << s << endl;
    }
    else
    {
        rs += s;
        cout << rs << endl;
    }
}

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

B - Informatics in MAC

解题思路:

如果有解,一定能只划分为两个区间。

从左向右遍历,实时维护前后缀的\(mex\)

代码:

#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;
const int N = 6010;

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n + 1), cl(n + 1), cr(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        cr[a[i]]++;
    }
    int l = 0;
    int r = 0;
    while (cr[r] > 0)
    {
        r++;
    }
    for (int i = 1; i < n; i++)
    {
        cl[a[i]]++;
        cr[a[i]]--;
        if (cr[a[i]] == 0)
        {
            if (r > a[i])
            {
                r = a[i] + 1;
            }
        }
        while (cl[l] > 0)
        {
            l++;
        }
        while (r > 0 && cr[r - 1] == 0)
        {
            r--;
        }
        if (l == r)
        {
            cout << 2 << endl;
            cout << 1 << ' ' << i << endl;
            cout << i + 1 << ' ' << n << endl;
            return;
        }
    }
    cout << -1 << endl;
}

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

C - Messenger in MAC

解题思路:

同样的元素集合,一定是按照\(b\)升序排列花费最小。

\(b\)进行升序排序,枚举左右两个端点,\(b\)的总和就确定了。取区间中尽量多的最小的\(a\),使得花费小于等于\(l\)

所以我们就是要维护区间中的\(a\)有序,然后尽量多取较小的\(a[i]\)

代码:

#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;
const int N = 6010;

void solve()
{
    ll n, m;
    cin >> n >> m;
    vector<pii> v(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i].fi >> v[i].se;
    }
    sort(v.begin(), v.end(), [&](auto a, auto b)
         { return a.se < b.se; });
    ll ans = 0;
    for (int i = 1; i <= n; i++)
    {
        // cost 维护了[l,r]的a的和减去一个b[i]
        ll cost = -v[i].se;
        priority_queue<int> q;
        for (int j = i; j <= n; j++)
        {
            q.push(v[j].fi);
            cost += v[j].fi;
            if (!q.empty() && cost + v[j].se > m)
            {
                cost -= q.top();
                q.pop();
            }
            ans = max(ans, (ll)q.size());
        }
    }
    cout << ans << endl;
}

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

D - Exam in MAC

解题思路:

考虑容斥:总对数 - 通过\(a[i]\)单个排除的对数 + 重复减去的对数。

对于\(a[i]:\)

  • 考虑\(x + y = a[i]\):\((0,a_i),(1, a_i-1),...,(\frac{a_i}{2},a_i -\frac{a_i}{2})\)
  • 考虑\(y - x = a_i:\)\((0,a_i),(1,a_i + 1),...,(c - a_i,c)\)

自身排除对数时有一个重复对。

不同\(a\)之间会重复的只有较小\(a_i\)\(y_i -x_i = a_i\)\(y_i + x_i = a_j\)这种情况。

如果\(a_i\)为奇数,那么\(y_i + x_i\)一定为奇数;如果\(a_i\)为偶数,那么\(y_i + x_i\)一定为偶数;

所以,较小的奇数一定会对较大的奇数造成一次对数删除重复,遍历记录奇偶个数即可。

举例:\(3 - 1= 2,3 + 1 = 4,4 - 2= 2, 4 +2 = 6\)

代码:

#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;
const int N = 6010;

void solve()
{
    ll n, c;
    cin >> n >> c;
    vector<ll> a(n + 1);
    // 计算总对数
    ll ans = (c + 2) * (c + 1) / 2;
    // 减去非法对
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        // x + y == a[i]
        ans -= a[i] / 2 + 1;
        // y - x == a[i]
        ans -= c - a[i] + 1;
    }
    // 加上重复减去的对
    vector<int> cnt(2);
    for (int i = 1; i <= n; i++)
    {
        cnt[a[i] % 2]++;
        ans += cnt[a[i] % 2];
    }
    cout << ans << endl;
}

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

E - Distance Learning Courses in MAC

解题思路:

首先考虑所有\(x_i = 0\)时的解法。

我们可以对于每一个二进制位,记录前缀和\(s[i][j]\),表示前\(i\)\(y_i\)中,第\(j\)位二进制位为\(1\)的情况出现了多少次。

\(s[i][j] \geq 2\),我们可以将一个留在第\(j\)位上;再取一个将第\(j\)位变成\(0\),然后将所有低数位变为\(1\),这样一定是最优的。

现在考虑\(x_i \neq 0\)的限制。

我们发现,此时如果存在\(s[i][j] \geq 2\),我们仍然可以留一个在第\(j\)位上,但是将当前位变\(0\),低位取\(1\)的操作收到了限制,因为数字变小有可能会小于下界,这是不合法的。

我们考虑先将所有相同的高数位都拆出来。

举例:\([1011,1100] \to 1000 +[0011,0100]\)

此时,我们可以确定拆出来的高位一定是这些位上的唯一解,接下来考虑低位。

我们发现,此时我们一定可以将最高数为上的\(1\)变为\(0\),然后低位全部取\(1\)。因为二者最高数位上的数不同,\(x_i < y_i,y_i -1 \geq x_i\)。我们不用再考虑\(x_i \neq 0\)对于解法的限制。这样就算是转化为了\(x_i=0\)的情况。

代码:

#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;
const int N = 6010;

void solve()
{
    int n;
    cin >> n;
    vector<int> x(n + 1), y(n + 1);
    vector<vector<int>> pre(n + 1, vector<int>(32)), res(n + 1, vector<int>(32));
    for (int i = 1; i <= n; i++)
    {
        scanf("%d %d", &x[i], &y[i]);
        int dif = -1;
        for (int j = 30; j >= 0; j--)
        {
            if ((x[i] >> j & 1) != (y[i] >> j & 1))
            {
                dif = j;
                break;
            }
        }
        for (int j = 30; j > dif; j--)
        {
            pre[i][j] = (x[i] >> j & 1);
        }
        x[i] = x[i] & ((1ll << (dif + 1)) - 1);
        y[i] = y[i] & ((1ll << (dif + 1)) - 1);
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 30; j >= 0; j--)
        {
            pre[i][j] += pre[i - 1][j];
        }
        for (int j = 30; j >= 0; j--)
        {
            res[i][j] = res[i - 1][j] + (y[i] >> j & 1);
        }
    }
    int q;
    cin >> q;
    while (q--)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        ll ans = 0;
        for (int i = 30; i >= 0; i--)
        {
            if (pre[r][i] - pre[l - 1][i] > 0)
            {
                ans += 1ll << i;
                if (res[r][i] - res[l - 1][i] == 0)
                {
                    continue;
                }
                else
                {
                    ans += (1ll << i) - 1;
                    break;
                }
            }
            else
            {
                if (res[r][i] - res[l - 1][i] == 0)
                {
                    continue;
                }
                else if (res[r][i] - res[l - 1][i] == 1)
                {
                    ans += 1ll << i;
                }
                else
                {
                    ans += (1ll << i) + ((1ll << i) - 1);
                    break;
                }
            }
        }
        printf("%lld ", ans);
    }
    puts("");
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
posted @ 2024-03-06 12:15  value0  阅读(62)  评论(0)    收藏  举报