9.27 模拟赛(NOIP十三连测 #10)

2024--梦熊&太戈--NOIP十三连测 #10【订正】 - 比赛 - 梦熊联盟 (mna.wang)

复盘

开 T1。差分转化。模拟了一下样例感觉方案好像是唯一确定的,不需要贪心/DP。但不太能证。

想了会感觉找不出反例。然后写完了。对拍没挂。用时不到 \(30\) 分钟。

T2。\(m \le 20\) 且数据随机感觉很好做的样子。

发现不会正解,但是有 \(50\) 的部分分很快想出来了。先写 \(50\)

T2 正解先放,看 T3。

数位 DP?好像不是。想到了 ABC363D,但好像没关系。

没关系吗?我可以通过 ABC363D 的方法,处理出所有 \(<n\) 的回文数,总共 \(\sqrt n\) 个,然后双指针(其实全存下来再双指针,直接做差判断另一个是否回文即可)。能过 \(n \le 10^{12}\)\(50\) 分!写!

然后对拍没挂。尝试卡常通过 \(n \le 10^{16}\)\(70\) 分但失败了。

开 T4。不是第二档部分分直接按题意模拟啊,这种红题难度的部分分给了 \(30\)??

第一档倍增也会。有 \(60\) 了。正确性显然所以没对拍。

\(260\) 了。冲 T2 正解。

我好像忘了数据随机。于是加了一些针对随机优化。一个比较重要的是将前面的数去重。然后跑自己造的 \(n = 10^6,m=20\) 的随机数据。

1.6s。

不是啊????????????我是不是过了。

显然没过。最终 \(100+50+60+60=270\)。T3 \(n \le 10^{16}\) 多过了一个点。没离谱挂分就是胜利!

其实 T2 只要再加一个优化就过了。但我好像想到了(?)但是感觉不太好写(?)所以没写。

总结

好的:

  • 想出了 T1。
  • 没挂分。

不足:

  • 不会乱搞。

题解

A. 小 C 玩扑克

注意到区间 \([i, r_i]\) 最多只会反转 \(1\) 次。

区间反转可以差分转化,然后变成单点修改 \(i\)\(r_i + 1\),直到差分序列全变成 \(0\)。令差分序列为 \(b\)

注意到每个区间是否需要反转是唯一确定的。考虑归纳证明。

对于 \(j\),我们希望让 \(b_j\) 最终变成 \(0\),也就是说如果 \(b_j\) 原来是 \(1\) 的话,需要执行奇数次与 \(j\) 有关的操作(与 \(j\) 有关指对 \(i = j\)\(i = r_j + 1\) 进行操作)。如果 \(b_j\) 原来是 \(0\) 则偶数。

我们考虑所有满足 \(r_i + 1 = j\)\(i\)。因为 \(i \le r_i\) 所以这些区间是否反转已经确定了,且我们假设到现在都没出现过问题。

求出这些 \(i\) 中有多少个反转过。如果这个数量的奇偶性与 \(b_j\) 不相同,那么 \(i = j\) 的操作必须进行一遍。

所以我们就唯一确定了每个区间是否反转。所以输入的 \(t_i\) 是诈骗,只在算答案的时候用了。

B. 小 C 写代码

显然这不是个贪心/DP题。我们只需要算每一行的最小代价,加和即可。

两种做法。

暴力+优化

代码比文字好理解。复杂度是 \(\mathcal O(n 2^{2m})\),但是数据随机所以非常快(0.3s)。

bool st[2 * N];
vector<int> v[N];

int solve() {
    for (int i = 0; i < 1 << m; ++ i )
        v[ppc(i)].push_back(i);

    int res = 0;

    for (int i = 1; i <= n; ++ i ) {
        int x = 0;

        for (int j = 0; j < m; ++ j ) {
            char c = getchar();
            while (c != '1' && c != '0') c = getchar(); 
            x = x * 2 + c - '0';
        }

        if (st[x]) res ++ ;
        else {
            int p = c;
            for (int j = 0; j <= m; ++ j )
                for (int k : v[j])
                    if (st[x ^ k]) {
                        p = min(p, j + 1);
                        break;
                    }
            st[x] = true;
            res += p;
        }
    }

    return res;
}

正解

\(f(S)\) 表示通过当前已经编写的代码,变成 \(S\) 的最小代价。初始 \(f(S) = c\),即第一种编写代码方式。

我们要支持对 \(f\) 进行操作:

  • 查某个特定的 \(f(S)\)
  • 编写一行新的代码,并修改所有 \(f(S)\)

注意到 \(f(S) \le m\)。所以第二种操作里,所有 \(f(S)\) 最多只会总共变化 \(\mathcal O(m2^m)\)

考虑搜索。类似 SPFA,只有当 \(S\)\(f\) 变优时进行松弛,若没变优直接停止搜索。

所以复杂度是 \(\mathcal O(m^22^m)\)。因为每个状态修改一个点后能得到 \(m\) 个不同的,所以又多了一个 \(m\)

提交记录 #633164 - 梦熊联盟 (mna.wang)

C. 对称数之和

考虑 \(\mathcal O(2^{\log_{10}n})\) 枚举每一位是否进位,处理出 \(m_i\) 表示最终的 \(a, b\) 需要满足 \(a,b\) 的第 \(i\) 位和为 \(m_i\)。这里因为已经考虑好进位了所以有可能 \(m_i \ge 10\)

分类讨论:

  • 如果 \(a, b\) 位数相同:

    • 如果 \(m\) 不回文那么答案是 \(0\)

    • 否则,每个对应位单独计算贡献,乘法原理即可。

      具体的,如果这一位两个数的和是 \(m_i\),那么:

      • 如果 \(m_i \le 9\):方案数是 \(m_i + 1\)。因为 \(a_i\) 可以取 \(0 \sim m_i\)
      • 如果 \(m_i > 10\):方案数是 \(9-(m_i-9)+1=19-m_i\)。因为 \(a_i\) 可以取 \(m_i - 9 \sim 9\)
  • 如果 \(a, b\) 位数不同:

    \(a, b\) 中必有一个与 \(m\) 有相同位数。不妨设为 \(a\),最后将方案数乘 \(2\)

    枚举 \(b\) 的位数(\(1 \sim m_i - 1\))。满足如上条件的 \(a, b\)在至多只有一个。因为如果存在那么必定是这种形式:

    这是一个例子。其实就是通过做差和回文转移得到的。

    得到这个后判断一遍是否所有条件都合法即可。

posted @ 2024-09-27 19:52  2huk  阅读(6)  评论(0编辑  收藏  举报