1 条题解
-
0
思路与算法
先顺序符号执行,将每个最终变量写成某个初始变量的正/反或常量。最终相等约束形成带奇偶关系的函数图,用带权并查集标记奇负环和连接U的分量,统计必须Unknown的人数。
正确性说明
符号执行严格复用当前右值,故保存的表达式等于完整程序的最终值。变量等式间边权为取反奇偶,同一分量若有奇负环则T/F都矛盾而U可行;连接U的分量也必须全U。其他分量的偶环或T/F常量可赋布尔值,无需U。每变量恰一个最终依赖,使一个弱分量不能同时含冲突的常量终点,因此这些必要条件也充分。
复杂度
O((n+m)α(n))时间、O(n)空间,含符号表达式、带权并查集与分量标记初始化;按大小合并。
易错点
赋值是顺序执行,不能把原操作直接当最终方程。逻辑非U仍U;排版的‐、−和半角-都作为取反操作串读取。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct DS { vector<int> p, s, x, bad; DS(int n) : p(n + 1), s(n + 1, 1), x(n + 1), bad(n + 1) { iota(p.begin(), p.end(), 0); } int find(int u) { if (p[u] == u) return u; int old = p[u]; p[u] = find(old); x[u] ^= x[old]; return p[u]; } void join(int a, int b, int w) { int u = find(a), v = find(b); w ^= x[a] ^ x[b]; if (u == v) { if (w) bad[u] = 1; return; } if (s[u] > s[v]) swap(u, v); p[u] = v; x[u] = w; s[v] += s[u]; bad[v] |= bad[u]; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int c, T; cin >> c >> T; while (T--) { int n, m; cin >> n >> m; vector<int> e(n + 1); iota(e.begin(), e.end(), 0); for (int t = 0; t < m; t++) { string op; int i, j; cin >> op >> i; if (op == "T") e[i] = n + 1; else if (op == "F") e[i] = -(n + 1); else if (op == "U") e[i] = n + 2; else { cin >> j; e[i] = op == "+" ? e[j] : -e[j]; } } DS d(n); for (int i = 1; i <= n; i++) if (abs(e[i]) <= n) d.join(i, abs(e[i]), e[i] < 0); for (int i = 1; i <= n; i++) if (abs(e[i]) == n + 2) d.bad[d.find(i)] = 1; int ans = 0; for (int i = 1; i <= n; i++) ans += d.bad[d.find(i)]; cout << ans << "\n"; } }
- 1