Codeforces Round 1113 (Div. 2)

vp 的,最近多练练脑筋急转弯,所以只有 A 到 D。

A. You Delete, I Delete

Alice 删 0 且要让 \(s\) 最大,Bob 删 1 且要让 \(s\) 最小,那显然应该删第一个 0 和第一个 1。

B. Merge to Match

\(a\)\(b\) 排序显然是需要的,下面提到的 \(a\)\(b\) 都是被排序过的。

注意到 \(a\)\(b\) 数组的每个元素都是不一样的,也就是说 \(b\) 数组的每一个元素一定都是被至少一次操作生成出来的,因此若要有解,至少需要有 \(n \ge 2m\)

现在考虑 \(b\) 的每个元素怎么被生成。对于 \(b\) 的每个元素,它一定由 \(a\) 数组的若干元素 \(\{a_{i_k}\}\) 经过操作生成,并且有 \(\min{a_{i_k}} \le b_k \le \max{a_{i_k}}\),因此对于每个 \(b_i\) 的生成,只需考虑对应 \(a\) 元素的极值。

从贪心的角度想,\(a_i ≤ b_i ≤ a_{n−m+i}\) 应该是一个比较自然的结论。

另外跟同学简单讨论了一下,不直接推出这个结论,只需正反扫描一遍 \(a\)\(b\) 拼起来并排序的数组,如果正反扫描过程中,\(a\) 元素的数量全程大于 \(b\) 元素的数量,也是可以的,因为这其实和上一段叙述的结论成立的条件是等价的。

个人体感这题比 C 难一点(

C. Maximize the Score

贪心吗?不太能,至少让“最长区间的长度最大”并不能保证最优解。反例很好构造,考虑:

\[\{2, n 个数, 1, 2, 3, n 个数, 3, 1\} \]

如果让“最长区间的长度最大”,会取到两个 \(1\) 之间的区间并消除,答案只能是 \(n^2\) 量级的;但如果把两个 \(2\) 之间的区间和两个 \(3\) 之间的区间消除,那答案是 \(2 n^2\) 量级的,在 \(n\) 充分大的情况下会更优,但 \(n\) 要是很小或者干脆等于 \(0\) 那可就难说了。

此时立即想到 dp,状态就是答案要的东西,即 \(\text{dp}[i]\) 表示 \([1, i]\) 区间的最大分数,\(\text{last}[i]\) 表示数字 \(i\) 第一次出现的位置,状态转移方程是直接的:

  • 如果 \(a[i]\) 前面没出现过,那就只能有 \(\text{dp}[i] = \text{dp}[i-1] + 1\)
  • 否则,可以尝试消除区间,即 \(\text{dp}[i] = \max (\text{dp}[i-1] + 1, \text{dp}[\text{last}[a[i]] - 1] + (i - \text{last}[a[i]] + 1)^2)\)

时空复杂度都是线性的。

D. Good Pair Queries

赛时虽然看到这个复杂度,意识到显然要先预处理两个串,维护一些统计量之后,再以 \(O(1)\) 的复杂度来回答查询,但脑子一片浆糊导致没想清楚。

从最简单的结论开始,显然众数相同意味着可以对整个子串直接消消乐,即众数相同是好子串的一个充分条件。现在考虑反面,如果两子串的众数出现偏移,会如何?

如果只是一些小偏差,比如这样:\(11111000\)\(11100000\),似乎不需要宣判死刑,因为可以通过这样的最小操作来实现众数扭转:

\[11\underline{11}1000, 11\underline{10}0000 \to 111000, 110000 \]

消除后的子串众数相同,因此这一对也是 good 的。

问题在于如何刻画这种影响?一个刻画是考虑两子串的“众数偏差”,为此可以预先计算每个位置上 \(00, 01, 10, 11\) 的个数。\(01\)\(10\) 会导致两子串的众数出现偏差,而 \(00\)\(11\) 可以通过我们上面的消除操作来消除一个偏差,或者保留以不引入众数偏差。

\(01\)\(10\) 的个数是 \(\text{diff}\)\(00\)\(11\) 的个数为 \(\text{common}\),当一个子串的 \(\text{common}\) 大于等于 \(\text{diff}\) 的时候,说明众数偏差可以被消除。

而进一步地,\(\text{diff}\) 还可以细分为 \(01\)\(10\),这两个可以互相组合内部消化,也可以消除偏差。

这样就刻画完毕了,直接对着写就行。核心代码:

int main(void){
    pre();

    int t;
    read(t);
    while (t--) {
        int n, q;
        cin >> n >> q;

        string s1, s2;
        cin >> s1 >> s2;

        vi diff_01(n + 1), diff_10(n + 1), common(n + 1);
        diff_01[0] = 0; diff_10[0] = 0; common[0] = 0;
        forn (i, 1, n + 1) {
            diff_01[i] = diff_01[i - 1] +  (s1[i - 1] < s2[i - 1]);
            diff_10[i] = diff_10[i - 1] +  (s1[i - 1] > s2[i - 1]);
            common[i]  = common[i - 1]  + !(s1[i - 1] ^ s2[i - 1]);
        }

        forn (i, 0, q) {
            int l, r;
            cin >> l >> r;

            int _diff_01 = diff_01[r] - diff_01[l - 1],
                _diff_10 = diff_10[r] - diff_10[l - 1],
                _common = common[r] - common[l - 1];

            if (_common >= abs(_diff_01 - _diff_10))
                cout << "YES" << endl;
            else
                cout << "NO" << endl;
        }
    }
}
posted @ 2026-08-04 16:50  Pride205  阅读(7)  评论(0)    收藏  举报