CSES Giant Pizza(2-SAT) 题意 有$n$条件, 每个条件有$2$个要求, 必须至少满足两个要求中的$1$个, 求存不存在满足所有条件的解
题解 这是2-SAT 问题, 条件$(a or b) == (a and b) or (a and ~b)$这样就可以把两个要求提炼成$a -> b or b -> a$的边关系, 只要在SCC中不出现$a$和$a$就一定可以满足条件
在 2-SAT 的推导图中,边 $U→V$ 表示逻辑关系:“如果选了 U,就必须选 V”。
如果我们把整个图缩点成一个 DAG(有向无环图),那么边 $SCC_A →SCC_B$ 依然表示:“如果选了 $SCC_A$ 里的点,就必须选 $SCC_B$ 里的点” 。
为了不引起矛盾,我们的原则是:尽量选“不强迫别人”的状态作为真值。
拓扑序靠前(源点):它指向很多后续节点,选它为“真”会触发一连串的“必须为真”。
拓扑序靠后(汇点):它不指向别人(或指向较少),选它为“真”非常安全。
结论: 在 $X$ 和 $¬X$ 两个状态中,谁的拓扑序更靠后(更接近汇点),谁就更有资格被赋值为“真”。
参考代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <climits> #include <cstddef> #include <queue> #include <type_traits> #pragma GCC optimize("Ofast" ) #include <bits/stdc++.h> using namespace std;#define i128 __int128 #define DEBUG #ifdef DEBUG #define de(x) cout << (#x) << " = " << (x) << endl; #define de2(x, y) cout << (#x) << " , " << (#y) << " = " << (x) << " ~ " << (y) << endl; #else #define de(x) #define de2(x, y) #endif #define enld endl #define endl '\n' #define fi(x) for(int i = 1; i <= x; ++i) #define fi0(x) for(int i = 0; i < x; ++i) #define fj(n) for(int j = 1; j <= n; ++j) #define caillo ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define PLEASE_AC return 0 typedef long long ll;#define int long long const int N = 2e5 + 10 ;int n, m;vector<int > adj[N*2 ]; int dfn[N], low[N], scc[N], sz[N], in_stack[N], sc, idf;stack<int > st; void record () { int v = st.top (); st.pop (); scc[v] = sc; in_stack[v] = 0 ; sz[sc]++; } void tarjan (int u) { dfn[u] = low[u] = ++idf; in_stack[u] = 1 ; st.push (u); for (auto v : adj[u]) { if (!dfn[v]) { tarjan (v); low[u] = min (low[u], low[v]); }else if (in_stack[v]) low[u] = min (low[u], dfn[v]); } if (dfn[u] == low[u]) { sc++; while (st.top () != u) record (); record (); } } signed main () { cin >> n >> m; auto get_node = [&](int x, char sign) { x--; return (sign == '+' ) ? x<<1 : x << 1 | 1 ; }; auto neg = [&](int node) {return node ^ 1 ;}; fi (n) { int x, y;char s1, s2; cin >> s1 >> x >> s2 >> y; int u = get_node (x, s1); int v = get_node (y, s2); adj[neg (u)].push_back (v); adj[neg (v)].push_back (u); } for (int i = 0 ;i < m*2 ;++i) { if (!dfn[i]) tarjan (i); } vector<char > ans (m+1 ) ; for (int i = 0 ;i < m; ++i){ if (scc[i<<1 ]==scc[i<<1 |1 ]) { cout << "IMPOSSIBLE\n" ; return 0 ; } ans[i] = (scc[i<<1 ] < scc[i<<1 |1 ]) ? '+' : '-' ; } for (int i = 0 ;i < m; ++i) cout << ans[i] << ' ' ; }