跳转至

Codeforces Round 1011 (Div. 2)

2025-03-22

这场原本有点窃喜,上了65分,但是今天中午直接掉快一百分。。该加训了孩子们。周末打了两天 wzry 。。。

比赛链接: https://codeforces.com/contest/2085

比较简单的一集,赛时写出了ABCD,official rank 742。

E 似乎过题人数没有比 D 少很多,之后再看看吧。

A

翻转后如果不相等,那么你一定可以通过一次操作,使得它们的大小关系反过来。

若相等,则为回文串,如果不是全为一样的字符,则一次操作也是够的。

void ChatGptDeepSeek()
{
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    vector<int> cnt(25 + 1);
    for (auto c : s)
        cnt[c - 'a']++;
    if (*max_element(cnt.begin(), cnt.end()) == n)
    {
        cout << "NO\n";
    }
    else
    {
        string t = s;
        reverse(t.begin(), t.end());
        if (k)
            cout << "YES\n";
        else if (s < t)
            cout << "YES\n";
        else
            cout << "No\n";
    }
}

B

有点难受了。我B怎么写了半小时。。。

其实有个简单的思路就是,不管咋样,都把数组合并成长度为 4 的,即把 \([4,n]\) 给合并,就会好讨论很多了。

我的略微有点麻烦。。。

void ChatGptDeepSeek()
{
    /*
    要满足最后一次操作为0
    那么在这前一次 里面不能有0
    在这前两次 就必须要有0

    0 0 -> 1
    0 1 -> 2

    实际上就是把数组里的0变没

    如果有0 就合并他们


    */
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++)
        cin >> a[i];
    if (count(a.begin(), a.end(), 0) == n)
    {
        cout << "3\n";
        cout << "3 " << n << '\n';
        cout << "1 2\n";
        cout << "1 2\n";
        return;
    }
    vector<pair<int, int>> ans;
    int l = 0;
    vector<int> b;
    for (int i = 0; i < a.size(); i++)
    {
        if (a[i] == 0)
        {
            int j = i + 1, cnt = 1;
            while (j < a.size() && a[j] == 0)
            {
                j++;
                cnt++;
            }
            if (b.empty())
            {
                if (j == a.size())
                {
                    ans.push_back({1, n - 1});
                    ans.push_back({1, 2});
                    b.push_back(1);
                    break;
                }
                else
                {
                    if (cnt == 1)
                    {
                        ans.push_back({1, 2});
                    }
                    else
                    {
                        ans.push_back({i + 1, i + cnt});
                        b.push_back(1);
                    }
                }
            }
            else
            {
                if (cnt == 1)
                {
                    ans.push_back({b.size(), b.size() + cnt});
                }
                else
                {
                    ans.push_back({b.size() + 1, b.size() + cnt});
                    b.push_back(1);
                }
            }
            i = j - 1;
        }
        else
            b.push_back(1);
    }
    // if (ans.empty())
    //     ans.push_back({1, n});
    if (b.size() > 1)
        ans.push_back({1, b.size()});
    cout << ans.size() << '\n';
    for (auto [x, y] : ans)
        cout << x << " " << y << '\n';
    // cout << '\n';
}

C

\((x+k)+(y+k)=(x+k)\oplus (y+k)\)

对于 \(x+y=x\oplus y\) ,那么 \(x\)\(y\) 必定不能有同为 \(1\) 的位 。所以 \(x\ne y\) 时显然不能成立。

所以有一种很简单的构造方式就是,使得 \(x\) , \(y\) 中较大的一个数字先达到 \(100000\) 的形式,这样它的最高位为 \(1\) , 而另一个数字只会小于它。

void ChatGptDeepSeek()
{
    /*
    (x+k) + (y+k) = (x+k)^(y+k)


    */
    int x, y;
    cin >> x >> y;
    if (x == y)
    {
        cout << "-1\n";
        return;
    }
    if (x < y)
        swap(x, y);
    long long n = 1LL << (__lg(x) + 1);
    long long k = n - x;
    assert((x + k) + (y + k) == ((x + k) ^ (y + k)));
    cout << k << '\n';
}

D

如果倒着看呢?就是你每次选的时候,数量必须满足 \(cnt(k+1)\le i\) ,只要你能一直满足这个条件,那么就可以一直随便拿。

也就是在 \(k+1\) 之后可以拿第一个,在 \(2k+2\) 之后可以拿第二个。

其实就可以直接贪心了,若当前位置可以多拿一个,则直接把这个拿走。否则比较一下之前拿了的,如果当前的值是比拿了的最小值大的话,那么可以把那个扔掉换成这个。

void ChatGptDeepSeek()
{
    int n, k;
    cin >> n >> k;
    vector<int> d(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> d[i];
    reverse(d.begin() + 1, d.end());
    priority_queue<int, vector<int>, greater<>> pq;
    for (int i = k + 1; i <= n; i++)
    {
        // cerr<<d[i]<<'\n';
        if (1LL * ((int)pq.size() + 1) * (k + 1) <= i)
            pq.push(d[i]);
        else
        {
            pq.push(d[i]);
            // cerr << pq.top() << '\n';
            pq.pop();
        }
    }
    // cerr<<'\n';
    long long ans = 0;
    while (!pq.empty())
    {
        ans += pq.top();
        // cerr<<ans<<'\n';
        pq.pop();
    }
    // cerr<<'\n';
    cout << ans << '\n';
}

E

六百六十六啊,怎么不看题解能写出来的啊。而且没有很久。大概三十多分钟。而且前面又是在进行无效思考。。

速度还是最重要的因素之一,只有简单题速度足够快且保证正确率,才能留下更多空间去思考后面的题,才能有机会写出更多的题目。

实际上如果这题能想到考虑 \(\sum a\)\(\sum b\) 的值的关系,就很好做了。由于是对同一个数字取模,所以 \(sum_a \equiv sum_b \pmod{k}\)

所以 \(k \mid sum_a-sum_b\) ,于是我们的 \(k\) 一定是 \(sum_a-sum_b\) 的一个因子,直接进行枚举即可。枚举因子的复杂度是 \(\sqrt{sum_a-sum_b}\)\(sum_a-sum_b\) 的最大值大概是 \(10^{10}\) ,但是一个数字的因子的数量实际上是很少的,相比它自身的范围。应该大概是 \(\log n\) 个因子吧。这个不需要证明,你只需要感觉就行,显然比 \(n\) 少很多吧。

void ChatGptDeepSeek()
{
    int n;
    cin >> n;
    vector<int> a(n + 1), b(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    for (int i = 1; i <= n; i++)
        cin >> b[i];
    sort(a.begin() + 1, a.end());
    sort(b.begin() + 1, b.end());
    if (a == b)
    {
        cout << "998244353\n";
        return;
    }
    /*
    k的最小的可能的值是 b中最大值+1
    但这远远不够的

    如果b中没有对应的a 那么必然要模了
    并且模了之后 对应的值的数量还不能变的

    那么比k最小值还小的数字 自然不可能会变了
    但这个根本不够

    x%k=A
    suma%k = sumb%k
    k | suma - sumb
    也就是说 可行的 k 的数量是有限的

    值是 1e10 枚举因子范围 1e5
    因子数量应该只有log个
    */
    long long s = 0;
    for (int i = 1; i <= n; i++)
    {
        s += a[i] - b[i];
    }
    if (s < 0)
    {
        cout << "-1\n";
        return;
    }
    auto check = [&](long long x)
    {
        if (x <= b[n] || x > 1000000000)
            return false;
        vector<int> c = a;
        for (int i = 1; i <= n; i++)
            c[i] %= x;
        sort(c.begin() + 1, c.end());
        return c == b;
    };
    for (long long i = 1; i * i <= s; i++)
    {
        if (s % i == 0)
        {
            if (check(i))
            {
                cout << i << '\n';
                return;
            }
            if (check(s / i))
            {
                cout << s / i << '\n';
                return;
            }
        }
    }
    cout << "-1\n";
}