思路:这道题,自己想的思路比别的博客稍微复杂一些,感觉像3-sat(虽然不知道3-sat是啥)
首先,u,v之间有限制,所以我们先按k=0和k=1分类
当k=0时,u,v两局必须要出一样的,然后再按Bob的u,v两局出的是否一样分类讨论
当k=1时,u,v两局必须要出一样的,然后再按Bob的u,v两局出的是否一样分类讨论
具体讨论见代码注释
#include
using namespace std;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
int n, m;
int head[N], tot;
int b[N];
int sccno[N], scc_cnt, dfn[N], low[N], idx;
stackst;
struct Edge {
int to;
int nxt;
}e[M];
void add(int u, int v) {
e[++tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
}
void dfs(int u) {
st.push(u);
dfn[u] = low[u] = ++idx;
for (int v,i = head[u]; i != -1; i = e[i].nxt) {
v = e[i].to;
if (!dfn[v]) {
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (!sccno[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
scc_cnt++;
while (1) {
int x = st.top();
st.pop();
sccno[x] = scc_cnt;
if (x == u)break;
}
}
}
bool find_scc() {
memset(sccno, 0, sizeof(sccno));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
scc_cnt = idx = 0;
for (int i = 1; i <= 3 * n; i++)
if (!dfn[i])dfs(i);
for (int i = 1; i <= n; i++)
if (sccno[i] == sccno[i + n] || sccno[i] == sccno[i + 2 * n] || sccno[i + n] == sccno[i + 2 * n])return false;
return true;
}
int main() {
int t;
scanf("%d", &t);
int ex = 0;
while (t--) {
memset(head, -1, sizeof(head));
tot = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
b[i]--;
}
bool flag=true;
for (int u,v,k,i = 1; i <= m; i++) {
scanf("%d%d%d", &u, &v, &k);
if (k == 0) { //u,v两局必须要出一样的
if (b[u] != b[v]) { //Bob的u,v两局出的不一样
if (b[u] == 0 && b[v] == 1||b[u]==1&&b[v]==0) { //Bob两局中,一局出石头,一局出布,所以Alice两局都只能出布,其他的都不能出,当第u局出石头或剪刀时,第u局必须出布;第v局同上
add(u, u + n);
add(u + 2 * n, u + n);
add(v, v + n);
add(v + 2 * n, v + n);
}
if (b[u] == 0 && b[v] == 2||b[u]==2&&b[v]==0) {
add(u + n, u);
add(u + 2 * n, u);
add(v + n, v);
add(v + 2 * n, v);
}
if (b[u] == 1 && b[v] == 2 || b[u] == 2 && b[v] == 1) {
add(u, u + 2 * n);
add(u + n, u + 2 * n);
add(v, v + 2 * n);
add(v + n, v + 2 * n);
}
}
else { //当Bob的u,v两局出的相同时
add(u + n * b[u], v + n * b[u]); //Alice两局都出跟Bob一样的
add(u + n*((b[u] + 1) % 3), v + n*((b[u] + 1) % 3)); //Alice两局都出能打败Bob的
}
}
else if (k == 1) { //Alice的u,v两局必须出不一样的
if (b[u] == b[v]) { //当Bob的u,v两局出的一样的,Alice的u,v两局必须一局出与Bob出一样的,另一局出能打败Bob的
add(u + b[u] * n, v + n * ((b[u] + 1) % 3));
add(u + n * ((b[u] + 1) % 3), v + b[u] * n);
add(v + b[u] * n, u + n * ((b[u] + 1) % 3));
add(v + n * ((b[u] + 1) % 3), u + b[u] * n);
}
else { //Bob的两局出的不一样,分类讨论
if (b[u] == 0 && b[v] == 1) {
add(u + n, v + 2 * n);
add(v + n, u);
}
if (b[u] == 0 && b[v] == 2) {
add(u, v + 2 * n);
add(v, u + n);
}
if (b[u] == 1 && b[v] == 2) {
add(u + 2 * n, v);
add(v + 2 * n, u + n);
}
if (b[u] == 1 && b[v] == 0) {
add(u + n, v);
add(v + n, u + 2 * n);
}
if (b[u] == 2 && b[v] == 0) {
add(u, v + n);
add(v, u + 2 * n);
}
if (b[u] == 2 && b[v] == 1) {
add(u + 2 * n, v + n);
add(v + 2 * n, u);
}
}
}
}
printf("Case #%d: ", ++ex);
printf(find_scc() ? "yes\n" : "no\n");
}
return 0;
}