题解:P11117 [ROI 2024] 交互式通道 (Day 2)

· · 题解

首先,判掉最后的图不合法的情况。

然后发现,黑黑、白白边可以删除,这些边在最后一定合法。

因此原图变成二分图。

要求:白色边不能被染黑。

最大的问题是:每个白点被染黑后再染白这段时间内不能存在白边,这是唯一的限制。

于是用有向边表示点与点的染色顺序限制,合法条件就是不存在环。使用 Tarjan 判环,Topo 排序输出方案即可。

这是线性的。

code

#include <stdint.h>
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
namespace fast_IO{
#define ld cin
#define jyt cout
}using namespace fast_IO;
#define REP(i, l, r) for (int i = l; i <= r; ++i)
#define PER(i, l, r) for (int i = l; i >= r; --i)
#define clean(name) memset(name, 0, sizeof(name))
#define memst(name, n) memset(name, 0, (n + 1) * sizeof(name[0]))
// #define int long long
constexpr int N = 2e5 + 7;
constexpr int inf = 1e9 + 7;
constexpr int P = 998244353;
namespace JoKing {
    int n, m, q, a[N], dfn[N], low[N], deg[N], dc, stc[N], top, vis[N]; bool flag = true;
    struct Edge {int u, v, w;} e[N];
    vector<int> G[N];
    inline void Tarjan(int x) {
        dfn[x] = low[x] = ++dc, stc[++top] = x, vis[x] = 1;
        for (int v : G[x]) {
            if (!dfn[v]) Tarjan(v), low[x] = min(low[x], low[v]);
            else if (vis[v]) low[x] = min(low[x], dfn[v]);
            if (!flag) return;
        }
        if (dfn[x] == low[x]) {
            if (stc[top] != x) flag = false;
            else vis[x] = 0, --top;
        }
    }
    signed main() {
        ld >> n >> m, q = 0, flag = true;
        REP(i, 1, m) ld >> e[i].u >> e[i].v >> e[i].w;
        REP(i, 1, n) ld >> a[i];
        REP(i, 1, m) if (a[e[i].u] ^ e[i].w && a[e[i].v] ^ e[i].w) return jyt << "NO\n";
        REP(i, 1, m) if (a[e[i].u] ^ a[e[i].v]) e[++q] = e[i]; m = q;
        REP(i, 1, m) {
            if (a[e[i].u]) swap(e[i].u, e[i].v);
            if (e[i].w) {
                G[e[i].v].emplace_back(e[i].u), ++deg[e[i].u];
            } else {
                G[e[i].u].emplace_back(e[i].v), ++deg[e[i].v];
            }
        }
        REP(i, 1, n) if (!dfn[i]) {Tarjan(i); if (!flag) break;}
        if (!flag) {
            jyt << "NO\n";
        } else {
            jyt << "YES\n";
            queue<int> Q; int tot = 0;
            REP(i, 1, n) if (tot += (a[i] ? 1 : 2), !deg[i]) Q.emplace(i);
            jyt << tot << '\n';
            while (!Q.empty()) {
                int x = Q.front(); Q.pop();
                if (!a[x]) jyt << x << ' ' << '1' << '\n' << x << ' ' << '0' << '\n';
                else jyt << x << ' ' << 1 << '\n';
                for (int v : G[x]) if (!(--deg[v])) Q.emplace(v);
            }
        }
        REP(i, 1, n) dfn[i] = low[i] = vis[i] = deg[i] = 0, G[i].clear(); dc = top = 0;
        return 0;
    }
}
signed main() {
#ifdef WYY
    freopen("files/code.in", "r", stdin);
    freopen("files/code.out", "w", stdout);
    freopen("files/code.err", "w", stderr);
#else
    // freopen("yui.in", "r", stdin);
    // freopen("yui.out", "w", stdout);
#endif
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 0; ld >> T;
    while (T--) JoKing::main();
    return 0;
}