Codeforces 1492E Almost Fault-Tolerant Database
Description
给定 $n$ 个长度为 $m$ 的串 $s_1, s_2, \cdots, s_n$,找到一个长度为 $m$ 的串 $ans$,使得 $ans$ 与任意 $s_i$ 至多有两个位置不同。如果无解,输出 No。
$nm \le 2.5 \times 10^5$
Solution
不妨先考虑令 $ans = s_1$,跑一遍,找出每一个串与 $ans$ 的最大差距 $\Delta$ 和这个串的编号 $id$。
如果 $\Delta \le 2$,直接输出 $ans$ 就好了。
如果 $\Delta > 4$,必然无解。
现在只剩下 $\Delta = 3, 4$ 的情况。
后者是容易解决的,因为 $s_1, s_{id}$ 与真正的答案必然各有 $2$ 个不同。那么对于 $s_1$ 和 $s_{id}$ 不同的 $4$ 个位置,枚举其中哪两个是和 $s_1$ 一样,哪两个是和 $s_{id}$ 一样,剩下的 $m - 4$ 个位置必然与它们都相同,只需要检查至多 $\binom{4}{2} = 6$ 遍就行了。
着重研究一下 $\Delta = 3$ 的情况。
设真正的答案与 $s_1$ 差距为 $a$,真正的答案与 $s_{id}$ 差距为 $b$。因为 $s_1$ 和 $s_{id}$ 已经有 $3$ 个位置不同,所以 $a+b \ge 3$。同时,因为 $a \le 2, b \le 2$,所以 $a+b \le 4$。由此可知,$a+b \in \{ 3, 4 \}$。
记 $s_1$ 与 $s_{id}$ 不同的三个位置为 $x, y, z$。可不可能存在另一个 $p \notin \{x, y, z\}$,使得 $s_1/s_{id}$ 不同于真正的答案呢?
这是 不可能 的,因为如果存在这样的 $p$,必然导致 $a+b \ge 2 + 3 = 5$,矛盾。
现在考虑 $S = \{ans_x, ans_y, ans_z\}$ 分别应该是什么。
因为 $s_1$ 和 $s_{id}$ 有 $3$ 个位置不同,可知 $S$ 中必然有一个元素与 $s_1$ 对应位置相同,$S$ 中也必然有另一个元素与 $s_{id}$ 对应位置相同。这是显然的,因为不这样会导致 $a/b \ge 3$。
不失一般性的,设 $ans_x = s_{1, x}$,$ans_y = s_{id, y}$,这时候可以先随便设 $ans_z = s_{1, z}$,跑一遍,如果发现了另一个串 $id'$ 与现在的 $ans$ 有 $3$ 个不同,就可以令 $ans_z = s_{id', z}$,再检查一遍就行了。
上述过程枚举 $x, y$ 有 $\binom{3}{2} \binom{2}{1} = 6$ 种情况,每种情况至多检查 $2$ 遍,运算量接近 $12nm$。
综上,时间复杂度 $O(knm)$,$k \le 12$。为了偷懒,我下面代码写得比较丑(做了些不必要的检查),运算量可以达到 $18nm$,但这依然不影响通过此题。
#include <bits/stdc++.h> using namespace std; const int N = 2.5e5 + 5; vector<int> s[N], ans; int n, m; void Print() { cout << "Yes\n"; for(int i = 1; i <= m; i++) cout << ans[i] << ' '; } int Comp(int idx) { int res = 0; for(int i = 1; i <= m; i++) { if(s[idx][i] != ans[i]) res++; } return res; } int Comp(int idx, vector<int> &pos) { int res = 0; pos.clear(); for(int i = 1; i <= m; i++) { if(s[idx][i] != ans[i]) { res++; pos.push_back(i); } } return res; } void Work(int &max_dif, int &pos) { max_dif = -1; for(int i = 1; i <= n; i++) { int cur_dif = Comp(i); if(cur_dif > max_dif) { max_dif = cur_dif; pos = i; } } } int main() { ios::sync_with_stdio(false); cin >> n >> m; for(int i = 1; i <= n; i++) { s[i].resize(m + 5); for(int j = 1; j <= m; j++) cin >> s[i][j]; } ans = s[1]; int mx, line; Work(mx, line); if(mx <= 2) { Print(); return 0; } else if(mx == 3) { vector<int> dif_pos; Comp(line, dif_pos); for(int chg = 1; chg < 7; chg++) { ans = s[1]; for(int i = 0; i < 3; i++) { if(chg >> i & 1) ans[dif_pos[i]] = s[line][dif_pos[i]]; } int new_mx, new_line; Work(new_mx, new_line); if(new_mx <= 2) { Print(); return 0; } else if(new_mx == 3) { vector<int> new_dif_pos; Comp(new_line, new_dif_pos); for(int i = 0; i < 3; i++) { int tmp = ans[new_dif_pos[i]]; ans[new_dif_pos[i]] = s[new_line][new_dif_pos[i]]; int final_mx, final_line; Work(final_mx, final_line); if(final_mx <= 2) { Print(); return 0; } ans[new_dif_pos[i]] = tmp; } } } } else if(mx == 4) { vector<int> dif_pos; Comp(line, dif_pos); for(int chg = 1; chg < 15; chg++) { ans = s[1]; for(int i = 0; i < 4; i++) { if(chg >> i & 1) ans[dif_pos[i]] = s[line][dif_pos[i]]; } int new_mx, new_line; Work(new_mx, new_line); if(new_mx <= 2) { Print(); return 0; } } } cout << "No\n"; return 0; }