Codeforces Round 1095 (Div. 2)补题

A. Disturbing Distribution

知识点:思维

思路:很容易发现如果 \(a,b !=1\) ,则有\(a*b>=a+b\),所以最小的代价应该是将所有不为 \(1\) 的数加起来,对于数值为 \(1\) 的数,如果他后面有大于 \(1\) 的数,可以一同被带走,但如果没有,就需要额外判断一下,也就是判断最末尾的元素是否为 \(1\) 即可

B. Everything Everywhere

知识点:数论,思维

思路:拿一个符合题中条件的序列来讲,每个数都可以整除 \(gcd\),所以这个序列中每个序列的差的绝对值也可以整除 \(gcd\),所以如果一个数大于另一个数,他们的差一定是 \(gcd\) 的整数倍,已知 \(max-min=gcd\),所以这个序列中只有两个元素,也就是我们只需要检查相邻的就可以了

C. Mental Monumental (Easy Version)

知识点:二分

思路:二分 \(mex\),如果 \(a_i\)\(mid\) 小的话就可以直接设 \(b_i\) 为一个很大的值,使得 \(a_i\) 不变,如果 \(a_i\)\(mid\) 大的话,通过 \(mod\) 转化为一个小的数,对于比 \(mid\) 大的数,这个数一定是越大越好,为什么呢 ?因为如果进行转化的话,一定有 \(a_i>2*c_i\),如果 \(b_i<=x/2\),\(a_i%b_i<b_i,所以c_i<=x/2\),如果 \(b_i>x/2\),\(a_i%b_i<x/2,所以c_i<=x/2\),综上,找出较大的数,找出不满足的数,比较大小,如果较大的数少就不满足条件

Code
点击查看代码
bool check(int mid, const vi &a)
{
    if (mid == 0)
        return 1;
    vb vis(mid, 0);
    for (int x : a)
    {
        if (x < mid)
        {
            vis[x] = 1;
        }
    }
    vi b;
    b.reserve(mid);
    for (int i = 0; i < mid; i++)
    {
        if (!vis[i])
        {
            b.pb(i);
        }
    }
    int len = b.size();
    if (len == 0)
        return 1;
    vi c;
    c.reserve(len);
    vb v(mid, 0);
    for (int i = n - 1; i >= 0 && c.size() < len; i--)
    {
        int now = a[i];
        if (now < mid && !v[now])
        {
            v[now] = 1;
        }
        else
        {
            c.pb(now);
        }
    }
    if (c.size() < len)
        return 0;
    for (int i = 0; i < len; i++)
    {
        if (c[len - 1 - i] < 2 * b[i] + 1)
        {
            return 0;
        }
    }
    return 1;
}

void solve()
{
    cin >> n;
    vi a(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    sort(all(a));
    l = 0, r = n;
    int res = 0;
    while (l <= r)
    {
        int mid = l + ((r - l) >> 1);
        if (check(mid, a))
        {
            res = mid;
            l = mid + 1;
        }
        else
        {
            r = mid - 1;
        }
    }
    cout << res << endl;
}

D. Reserved Reversals

知识点:思维

思路:如果天然有序直接输出。手模发现,我们可以让奇数在一边,偶数在一边,如果我们能对奇偶数内部分别排好序,那么整个序列就排好序了,拿奇数来说,如果存在逆序对,我们需要一个偶数来帮他们来转换,所以如果天然乱序且只有偶数或只有奇数的情况直接否定,这个偶数有两种选择,比这两个奇数都大或都小,如果有这样的偶数,就可以转换,如果没有就不可以转换。按照这个思路我们需要找出奇数和偶数中所有的逆序对,最坏情况是 \(O(n^2)\) 的,时间复杂度太高。再考虑如果存在一个奇数比最大的偶数大,一个奇数比最小的偶数小,并且他们是逆序对,这种情况直接否定,偶数也这样处理,如果没有这种情况就是可以使得数组有序的。找到最左边且大于最大偶数的奇数,同时找到最右边且小于最小偶数的奇数,判断他们的位置是否为逆序对,对于偶数也这样操作,验证这两种情况,存在一种就为否,都不存在就为 \(YES\)

Code
点击查看代码
void solve()
{
    cin >> n;
    vi a(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
    }
    if (is_sorted(all(a)))
    {
        cout << "YES" << endl;
        return;
    }
    int mxji = -1, miji = 1e9, mxou = -1, miou = 1e9;
    int ji = 0, ou = 0;
    for (int x : a)
    {
        if (x & 1)
        {
            mxji = max(mxji, x);
            miji = min(miji, x);
            ji = 1;
        }
        else
        {
            mxou = max(mxou, x);
            miou = min(miou, x);
            ou = 1;
        }
    }
    if (ji && ou)
    {
    }
    else
    {
        cout << "NO" << endl;
        return;
    }
    int ok = 0;
    int da = -1, xiao = -1;
    for (int i = 0; i < n; i++)
    {
        if (!ok && da == -1 && a[i] % 2 == 0 && a[i] > mxji)
        {
            da = i;
        }
        if (a[i] % 2 == 0 && a[i] < miji)
        {
            xiao = i;
        }
    }
    if (xiao > da && xiao != -1 && da != -1)
    {
        cout << "NO" << endl;
        return;
    }
    ok = 0;
    da = -1, xiao = -1;
    for (int i = 0; i < n; i++)
    {
        if (!ok && da == -1 && a[i] % 2 != 0 && a[i] > mxou)
        {
            da = i;
        }
        if (a[i] % 2 != 0 && a[i] < miou)
        {
            xiao = i;
        }
    }
    if (xiao > da && xiao != -1 && da != -1)
    {
        cout << "NO" << endl;
        return;
    }
    cout << "YES" << endl;
}
posted @ 2026-05-01 18:36  Lambda_L  阅读(112)  评论(0)    收藏  举报