CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!)

发布时间 2023-09-20 13:49:51作者: value0

CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!)

A. MEXanized Array

解题思路:

  1. 如果\(k > (x + 1) || k > n\)那么我们\(MEX\)都一定无法得到\(k\).
    • \(k > (x + 1)\),则我们取不到\(k-1\).
    • \(k > n\),我们同样取不到\(k-1\).
  2. 否则,我们选取\(0 \sim (k - 1)\)各一次,后选取不等于\(k\)的最大数即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
const int M = 2 * M;
typedef pair<int, int> pii;
#define fi first
#define se second

void solve()
{
    int n, k, x;
    scanf("%d %d %d", &n, &k, &x);
    if (k > (x + 1) || k > n)
    {
        puts("-1");
        return;
    }
    ll cnt = 0;
    ll ans = 0;
    for (int i = 0; i < n; i++)
    {
        ans += cnt;
        if (cnt < k - 1)
        {
            cnt++;
        }
        else if (x > k)
        {
            cnt = x;
        }
    }
    printf("%lld\n", ans);
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

B. Friendly Arrays

解题思路:

对于全体或运算:

  1. 如果\(n\)是奇数,对于异或和,若我们原数位上有奇数个\(1\)那么或上这一位不会有影响。反之,或上这一位\(1\)能使答案多出这一位的贡献,所以我们的操作只会使得异或和增大,不会使其减小:
    • 对于最小异或和,不用进行任何操作
    • 对于最大异或和,在原异或和基础上,添加上\(b_i\)中存在的所有位的贡献。
  2. 如果\(n\)是偶数,对于异或和,若我们原数位上有偶数个\(1\)那么或上这一位不会有影响。反之,或上这一位\(1\)能使答案减去这一位的贡献,所以我们的操作只会使得异或和减小,不会使其增大:
    • 对于最小异或和,在原异或和的基础上,减去\(b_i\)中存在的所有位的贡献。
    • 对于最大异或和,不用进行任何操作。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
const int M = 2 * M;
typedef pair<int, int> pii;
#define fi first
#define se second
int n, m;
void solve()
{
    scanf("%d %d", &n, &m);
    vector<int> a(n + 1), b(m + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= m; i++)
    {
        scanf("%d", &b[i]);
    }
    if (n & 1)
    {
        vector<int> v(40);
        ll res = 0;
        for (int i = 1; i <= n; i++)
        {
            res ^= a[i];
        }
        for (int i = 1; i <= m; i++)
        {
            for (int j = 30; j >= 0; j--)
            {
                if (b[i] >> j & 1)
                {
                    v[j] = 1;
                }
            }
        }
        ll ans = res;
        for (int i = 30; i >= 0; i--)
        {
            if (v[i])
            {
                ans |= (1 << i);
            }
        }
        printf("%lld %lld\n", res, ans);
    }
    else
    {
        ll res = 0;
        for (int i = 1; i <= n; i++)
        {
            res ^= a[i];
        }
        vector<int> v(40);
        for (int i = 1; i <= m; i++)
        {
            for (int j = 30; j >= 0; j--)
            {
                if (b[i] >> j & 1)
                {
                    v[j] = 1;
                }
            }
        }
        ll ans = res;
        for (int i = 30; i >= 0; i--)
        {
            if ((res >> i & 1) && v[i])
            {
                ans -= (1 << i);
            }
        }
        printf("%lld %lld\n", ans, res);
    }
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

C. Colorful Table

解题思路:

\(a_i\)会在第\(i\)行和第\(i\)列被进行比对。若\(a_i 和a_j\)进比对,较小的那个数会占据第\(i \ or\ j\)行和第\(i \ or\ j\)列。这个框柱这个十字的长和宽,就是当前最大空白边框的大小。

所以,我们排个序,从最小值开始遍历,边加上当前最大边框大小,边更新边框大小即可。

比如最开始我们的边框由上下左右四个方向界定,\(u = 1,d = n ,l = 1,r =n\).如果我们最小值是\(a_1\),那么第一行和第一列会被最小值覆盖,长宽贡献为\((d - u) + (r - l)\)

而之后的值在覆盖时会缺少这一行和这一列,所以\(u ++ ,u = 2.l ++,l = 2\).

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
const int M = 2 * M;
typedef pair<int, int> pii;
#define fi first
#define se second
int n, m, k;
void solve()
{
    scanf("%d %d", &n, &k);
    vector<pii> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i].fi);
        a[i].se = i;
    }
    sort(a.begin() + 1, a.end());
    vector<bool> row(n + 1), col(n + 1);
    vector<ll> ans(k + 1);
    ll u = 1;
    ll d = n;
    ll l = 1;
    ll r = n;
    for (int i = 1; i <= n; i++)
    {
        int idx = a[i].se;
        int val = a[i].fi;
        while (row[u])
        {
            u++;
        }
        while (row[d])
        {
            d--;
        }
        while (col[l])
        {
            l++;
        }
        while (col[r])
        {
            r--;
        }
        ll t = (d - u + 1) + (r - l + 1);
        if (idx == u)
        {
            u++;
        }
        else if (idx == d)
        {
            d--;
        }
        row[idx] = true;
        if (idx == l)
        {
            l++;
        }
        else if (idx == r)
        {
            r--;
        }
        col[idx] = true;
        ans[val] = max(ans[val], t);
    }
    for (int i = 1; i <= k; i++)
    {
        printf("%lld ", ans[i]);
    }
    puts("");
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

D. Prefix Purchase

解题思路:

如果\(i < j\),并且\(c_i >= c_j\),那么我们一定选\(c_j\)

由此可知,如果答案中出现\(c_k >= c_j\)的情况,那么一定是\(k > j\).

所以,答案选择序列的\(c_i\)序列一定是一个单调递增序列。我们这里可以用单调栈处理。

然后我们就可以遍历栈内元素,我们后选取的\(c_j\)一定大于之前选的\(c_i\)。要求最大字典序,那么我们可以计算在选取\(c\)的最大次数不变的情况下,我们能将多少\(c_i\)转变为\(c_j\).

假设我们现在\(res = \lfloor \frac k {c_i} \rfloor ,k = k \% c_i\)。最大转变数即为\(cnt = min(\lfloor \frac {k} {c_{i + 1} - c_i} \rfloor,res)\),这说明,此时我们能用\(cnt\)\(c_i\)换取同等数量的\(c_{i + 1}\),由于我们此时是在上述处理好后的单调栈中进行的遍历,所以同等数量下,一定是选越后面的越优,所以我们换。换的操作记得减去前面的加上后面的。

依次遍历完即可。

时间复杂度\(O(n)\).

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
const int M = 2 * M;
typedef pair<int, int> pii;
#define fi first
#define se second
int n, m, k;
// 次数优先
// 其次越远越好

bool cmp(pii a, pii b)
{
    if (a.fi != b.fi)
    {
        return a.fi < b.fi;
    }
    else
    {
        return a.se > b.se;
    }
}
void solve()
{
    scanf("%d", &n);
    vector<int> a(n + 1), ans(n + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    scanf("%d", &k);
    vector<int> q(n + 1);
    int hh = 1;
    int tt = 0;
    for (int i = 1; i <= n; i++)
    {
        while (hh <= tt && a[q[tt]] >= a[i])
        {
            tt--;
        }
        q[++tt] = i;
    }
    ll lst = 1e9;
    ll idx = 0;
    for (int i = 1; i <= tt; i++)
    {
        ll cnt = k / (a[q[i]] - a[q[i - 1]]);
        lst = min(cnt, lst);
        k -= (a[q[i]] - a[q[i - 1]]) * lst;
        for (int j = idx + 1; j <= q[i]; j++)
        {
            ans[j] = lst;
        }
        idx = q[i];
    }
    for (int i = 1; i <= n; i++)
    {
        printf("%d ", ans[i]);
    }
    puts("");
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}