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;
}

浙公网安备 33010602011771号