1 条题解
-
0
题目解析
问题重述
给定两个 的矩阵 和 ,元素范围 。允许两种操作:
- 行与操作:选择行 和整数 ,将该行每个元素 替换为
- 列或操作:选择列 和整数 ,将该列每个元素 替换为
问能否通过任意次操作将 变为 。
核心思路
1. 按位独立处理
由于位运算(AND、OR)的每一位是独立的,我们可以分别考虑每个二进制位( 到 位,因为 )。对于每个位,问题转化为:
- 给定两个二进制矩阵 和 (只包含 和 )
- 操作:
- 将某行全部设为 (对应 AND 操作,取 该位为 )
- 将某列全部设为 (对应 OR 操作,取 该位为 )
2. 必要操作分析
对于每个位置 ,比较 和 (当前位):
- 若 :该位从 变 ,只能通过列 OR 操作实现,因此第 列必须执行 OR 操作
- 若 :该位从 变 ,只能通过行 AND 操作实现,因此第 行必须执行 AND 操作
- 若 :没有强制要求,但后续操作可能改变它
3. 操作之间的依赖关系
考虑一个位置 且 :
- 如果对第 列执行了 OR 操作,该位置会变成 ,不符合要求
- 因此,若对第 列执行 OR 操作,则必须随后对第 行执行 AND 操作,将其改回
- 这形成依赖:列 OR 行 AND
类似地,若 :
- 如果对第 行执行 AND 操作,该位置会变成 ,不符合要求
- 因此,若对第 行执行 AND 操作,则必须随后对第 列执行 OR 操作
- 这形成依赖:行 AND 列 OR
4. 构建依赖图
- 创建 个节点:
- 节点 表示行 AND 操作
- 节点 表示列 OR 操作
- 添加有向边:
- 若 :添加边 (列 OR 必须先于行 AND)
- 若 :添加边 (行 AND 必须先于列 OR)
5. 必须执行的操作
- 若有位置 ,则行 的 AND 操作必须执行
- 若有位置 ,则列 的 OR 操作必须执行
6. 可行性判断
从每个必须执行的操作节点出发,检查是否存在有向环:
- 如果存在环,意味着操作之间存在循环依赖,无法满足所有要求 → 该位不可行
- 如果没有环,则可以按拓扑序执行操作,得到正确结果
环检测使用三色 DFS:
0:未访问1:正在 DFS 栈中(当前路径)2:已完全处理
若在 DFS 中遇到状态为
1的节点,说明找到环。7. 最终答案
所有 个位都可行时输出
Yes,否则输出No。算法步骤
- 对于每个测试用例:
- 对于每个位 ():
- 构建 个节点的图
- 遍历所有 个位置:
- 比较 和 的第 位
- 记录必须执行的行/列操作
- 根据 的当前位添加依赖边
- 从每个必须执行的操作节点出发进行 DFS 环检测
- 如果有环,该位不可行,输出
No
- 所有位都可行,输出
Yes
示例验证
例2:
初始 :
10 10 42 42:
21 21 21 21二进制表示(低位在右):
- ,,
按位分析:
- 对于第 位(): 全是 , 全是 → 需要列 OR
- 无环 → 可行
所有位都可行 →
Yes时间复杂度
- 每位 建图 + DFS
- 共 位,,
- 总复杂度 $O(t \cdot 30 \cdot n \cdot m) \approx 3 \times 10^6$,非常快
C++ 代码
#include <bits/stdc++.h> using namespace std; struct Graph { int V; vector<vector<int>> g; vector<int> color; bool dfs(int v) { if (color[v] != 0) return false; color[v] = 1; bool has_cycle = false; for (int u : g[v]) { if (color[u] == 2) continue; if (color[u] == 0) { has_cycle |= dfs(u); } else { has_cycle = true; } } color[v] = 2; return has_cycle; } void add_edge(int x, int y) { g[x].push_back(y); } Graph(int V) : V(V), g(V), color(V, 0) {} }; int get_bit(int x, int k) { return (x >> k) & 1; } bool check(const vector<vector<int>>& A, const vector<vector<int>>& B, int k) { int n = A.size(); int m = A[0].size(); vector<bool> must_row(n, false); vector<bool> must_col(m, false); Graph G(n + m); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { int a_bit = get_bit(A[i][j], k); int b_bit = get_bit(B[i][j], k); // 必须执行的操作 if (a_bit != b_bit) { if (b_bit == 0) must_row[i] = true; else must_col[j] = true; } // 添加依赖边 if (b_bit == 0) { // B[i][j] = 0: 列 OR 必须先于行 AND G.add_edge(j + n, i); } else { // B[i][j] = 1: 行 AND 必须先于列 OR G.add_edge(i, j + n); } } } // 从必须执行的操作出发,检查是否有环 for (int i = 0; i < n; i++) { if (must_row[i] && G.dfs(i)) return false; } for (int j = 0; j < m; j++) { if (must_col[j] && G.dfs(j + n)) return false; } return true; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { int n, m; cin >> n >> m; vector<vector<int>> A(n, vector<int>(m)); vector<vector<int>> B(n, vector<int>(m)); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> A[i][j]; } } for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> B[i][j]; } } bool ok = true; for (int k = 0; k < 30; k++) { if (!check(A, B, k)) { ok = false; break; } } cout << (ok ? "Yes" : "No") << '\n'; } return 0; }关键点总结
- 按位独立:位运算的每一位独立,降低问题复杂度
- 二元操作:每位上只有 AND 行(设 )和 OR 列(设 )两种操作
- 依赖关系:根据目标值 确定操作顺序约束
- 有向图环检测:必须执行的操作若导致循环依赖,则不可行
- 三色 DFS:高效检测有向环,同时避免重复处理
注意事项
- 每个操作可以应用多次,但后一次覆盖前一次,所以只需考虑最后是否执行
- 操作顺序很重要:先执行的操作可能被后续操作覆盖
- ,因此 完全可以接受
- 使用
cin/cout时记得ios::sync_with_stdio(false)加速
- 1
信息
- ID
- 7069
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者