推性质转为2-sat问题:26暑杭电2-01

1001 xyz 问题

image-20260725133257463

我们发现这个op有3个选择,是个3-sat问题,非常麻烦。

我们分析一下性质:

image-20260725104317123

发现:

  1. y=1,z=0y=1,z=0 时,op只能是 ^&
  2. 否则,op选 | 一定比 ^ 更优,所以op只能是 |&

现在op必然是二选一了

然后我们就暴力枚举4种情况:

如果 x=i,op=jx=i,op=j 不成立,则:

  • x=1ix=1-iop=1jop=1-j 至少一个成立

就转化为2-sat问题了。


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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#include<bits/stdc++.h>
#include<cassert>
//#include<atcoder/twosat>
using namespace std;
namespace atcoder {
namespace internal {
template <class E> struct csr {
std::vector<int> start;
std::vector<E> elist;
explicit csr(int n, const std::vector<std::pair<int, E>>& edges)
: start(n + 1), elist(edges.size()) {
for (auto e : edges) {
start[e.first + 1]++;
}
for (int i = 1; i <= n; i++) {
start[i] += start[i - 1];
}
auto counter = start;
for (auto e : edges) {
elist[counter[e.first]++] = e.second;
}
}
};
// Reference:
// R. Tarjan,
// Depth-First Search and Linear Graph Algorithms
struct scc_graph {
public:
explicit scc_graph(int n) : _n(n) {}

int num_vertices() {
return _n;
}

void add_edge(int from, int to) {
edges.push_back({from, {to}});
}

// @return pair of (# of scc, scc id)
std::pair<int, std::vector<int>> scc_ids() {
auto g = csr<edge>(_n, edges);
int now_ord = 0, group_num = 0;
std::vector<int> visited, low(_n), ord(_n, -1), ids(_n);
visited.reserve(_n);
auto dfs = [&](auto self, int v) -> void {
low[v] = ord[v] = now_ord++;
visited.push_back(v);
for (int i = g.start[v]; i < g.start[v + 1]; i++) {
auto to = g.elist[i].to;
if (ord[to] == -1) {
self(self, to);
low[v] = std::min(low[v], low[to]);
} else {
low[v] = std::min(low[v], ord[to]);
}
}
if (low[v] == ord[v]) {
while (true) {
int u = visited.back();
visited.pop_back();
ord[u] = _n;
ids[u] = group_num;
if (u == v) break;
}
group_num++;
}
};
for (int i = 0; i < _n; i++) {
if (ord[i] == -1) dfs(dfs, i);
}
for (auto& x : ids) {
x = group_num - 1 - x;
}
return {group_num, ids};
}

std::vector<std::vector<int>> scc() {
auto ids = scc_ids();
int group_num = ids.first;
std::vector<int> counts(group_num);
for (auto x : ids.second) counts[x]++;
std::vector<std::vector<int>> groups(ids.first);
for (int i = 0; i < group_num; i++) {
groups[i].reserve(counts[i]);
}
for (int i = 0; i < _n; i++) {
groups[ids.second[i]].push_back(i);
}
return groups;
}

private:
int _n;
struct edge {
int to;
};
std::vector<std::pair<int, edge>> edges;
};

}
// namespace internal
// Reference:
// B. Aspvall, M. Plass, and R. Tarjan,
// A Linear-Time Algorithm for Testing the Truth of Certain Quantified Boolean
// Formulas
struct two_sat {
public:
two_sat() : _n(0), scc(0) {}
explicit two_sat(int n) : _n(n), _answer(n), scc(2 * n) {}

void add_clause(int i, bool f, int j, bool g) {
assert(0 <= i && i < _n);
assert(0 <= j && j < _n);
scc.add_edge(2 * i + (f ? 0 : 1), 2 * j + (g ? 1 : 0));
scc.add_edge(2 * j + (g ? 0 : 1), 2 * i + (f ? 1 : 0));
}
bool satisfiable() {
auto id = scc.scc_ids().second;
for (int i = 0; i < _n; i++) {
if (id[2 * i] == id[2 * i + 1]) return false;
_answer[i] = id[2 * i] < id[2 * i + 1];
}
return true;
}
std::vector<bool> answer() {
return _answer;
}

private:
int _n;
std::vector<bool> _answer;
internal::scc_graph scc;
};

} // namespace atcoder
using namespace atcoder;
#ifdef LOCAL
#define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
//#define int long long

inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
//#define N
struct node {
int i, j, y, z;
};
int n, m, i, j, k, T;
int y, z, n1, n2;

signed main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
// srand(time(NULL));
T = read();
while(T--) {
n1 = read(); n2 = read(); m = read();
vector<int>flg(n1 + n2 + 10);
vector<node>v;
two_sat ts(n1 + n2 + 5);
for(k = 1; k <= m; ++k) {
i = read(); j = read(); y = read(); z = read();
if(y == 1 && z == 0) flg[j] = 1;
v.pb({i, j, y, z});
}
auto No = [&] (int i, int x, int j, int y) -> void {
debug("%d == %d || %d == %d\n", i, 1 - x, j + n1, 1 - y);
ts.add_clause(i, (bool)(1 - x), j + n1, (bool)(1 - y));
};
for(auto t : v) {
i = t.i; j = t.j; y = t.y; z = t.z;
if(flg[j]) {
for(k = 0; k <= 1; ++k) {
if((k ^ y) != z) No(i, k, j, 0);
if((k & y) != z) No(i, k, j, 1);
}
}
else {
for(k = 0; k <= 1; ++k) {
if((k | y) != z) No(i, k, j, 0);
if((k & y) != z) No(i, k, j, 1);
}
}
debug("---(%d)--\n", flg[j]);
}
if(ts.satisfiable()) {
printf("YES\n");
vector<bool>ans = ts.answer();
for(i = 1; i <= n1; ++i) {
auto t = ans[i];
printf(t ? "1" : "0");
}
printf("\n");
for(i = 1; i <= n2; ++i) {
auto t = ans[i + n1];
if(flg[i]) printf(t ? "&" : "^");
else printf(t ? "&" : "|");
}
printf("\n");
}
else printf("NO\n");
}

return 0;
}