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\)在至多只有一个。因为如果存在那么必定是这种形式:
这是一个例子。其实就是通过做差和回文转移得到的。
得到这个后判断一遍是否所有条件都合法即可。