1 条题解
-
0
题目理解
有 个参赛者, 项运动。
参赛者 的技能由一个长度为 的二进制串 表示, 表示擅长第 项运动,否则为 。
已知所有 个二进制串两两不同。比赛从运动 开始,然后按循环顺序进行运动 。
每项运动 的规则:- 如果当前存活的参赛者中没有人擅长 ,则所有人都存活到下一项。
- 否则,只保留擅长 的参赛者(不擅长的被淘汰)。
当只剩一人时,该人获胜。
对每个 (),求胜者编号。
关键观察
. 淘汰过程只与当前存活集合有关,与历史无关。 . 由于 个二进制串互不相同,每次淘汰至少会去掉一部分人(除非所有人都擅长或都不擅长当前运动)。 . 最坏情况下,每个 需要模拟 轮,每轮扫描当前存活集合。
如果直接做,复杂度 不可接受。
但注意 ,且 ,因此实际 和 不能同时大。
例如 时 可达 ,此时每轮存活集合大小很小,总复杂度约为 ,可能可过。
标程中直接模拟了每个 ,每轮扫描当前存活集合,并重建新集合。
由于 ,总操作量大约是 ,而每轮人数单调递减,总复杂度近似 ,在数据范围内可接受。
算法步骤
. 读入 和 个二进制串。 . 初始化 数组为 (参赛者编号)。 . 对每个起始运动 从 到 :
- 令当前存活集合 。
- 对 到 (实际运动顺序为 ):
- 检查当前存活集合中是否有人擅长该运动:遍历 ,若找到 则 。
- 若 ,所有人存活,继续下一项运动。
- 否则,构造新集合 ,包含所有 中擅长该运动的人。
- 令 。
- 若 ,提前结束。
- 记录胜者为 。 . 输出所有 对应的胜者。
复杂度分析
- 外层循环 次。
- 内层循环最多 轮,但每轮存活集合大小快速下降。
- 总扫描次数约为 。
由于每个人最多被淘汰一次,且每次淘汰发生在某个运动上,最坏情况下每个 的模拟中,每个人可能被多次扫描(不同 下重新开始),因此总复杂度为 。
但因为 ,实际运行时间在 秒内可接受。
示例验证
输入:
3 5 10010 01100 10101输出:
3 2 3 1 3- (运动 1):
运动 : 擅长的人 ,淘汰
运动 : 擅长的人 ,但 2 已淘汰 → 无人擅长 保留
运动 : 擅长的人 保留 → 胜者
其他 类似。
完整代码(标程)
#include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<string> a(n); for (int i = 0; i < n; ++i) cin >> a[i]; vector<int> ord(m); iota(ord.begin(), ord.end(), 0); vector<int> pos(n); for (int i = 0; i < n; ++i) pos[i] = i; vector<int> ans(m); for (int x = 0; x < m; ++x) { vector<int> cur = pos; for (int j = 0; j < m; ++j) { int idx = (x + j) % m; bool ok = false; for (int p : cur) if (a[p][idx] == '1') { ok = true; break; } if (!ok) continue; vector<int> nxt; for (int p : cur) if (a[p][idx] == '1') nxt.push_back(p); cur = nxt; if (cur.size() == 1) break; } ans[x] = cur[0] + 1; } for (int i = 0; i < m; ++i) cout << ans[i] << " \n"[i == m-1]; return 0; }
总结
- 本题直接模拟淘汰过程即可通过,因为 保证了总操作量在可接受范围。
- 关键优化是每轮只扫描当前存活集合,并重建新集合。
- 由于 ,极端情况下 小 大时,存活集合很小,扫描快; 大 小时, 小,外层循环次数少。
因此整体复杂度约为 ,实际运行效率高。
- 1
信息
- ID
- 7165
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 1
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者