【每日一题】21.边的染色 (DFS连通图 + 思维)

补题链接:Here

思维不够,看到这种陌生的题目无从下手.

这题应该做过一次的人会觉得它其实并不难.
主要思想:把边权->点权.
这样做的好处是,无论你怎么分配点权,在环内的异或值一定为 \(0\) (前提是环内的一定合法.)
做题步骤也是围绕这些性质来的.

  1. 首先判断给定的点是否有矛盾,就是你给一个点赋值,它假如是环的话,有矛盾,那么它的异或值为 \(1\) .然后你这个点的异或值会和你之前给定它的异或值不同.
  2. 找有多少联通块,不同的联通块是独立事件,用乘法原理.然后点权的设置,其实本身存在重复,就是你把所有点取反和不取反,这两种情况的边权是一样的.然后本身没有限制,假设联通块大小是 \(k\) 那么就是\(2^k\) .去除重复就是 \(2^{k - 1}\)
  3. 考虑限制,一旦给一段连续的限制,你给头部元素赋值 \(1/0\) ,它这一段都是确定的,换句话来说,它这一段的贡献只有\(2\) 所以我们应该把之前多算的贡献消掉,假如这段联通块大小是 \(p\) ,那么我们必须把 \(ans/=2^{p-1}\)
using ll      = long long;
const int mod = 998244353;
const int N   = 1e5 + 10;
vector<pair<int, int>> e[N]; // u,v,w
int color[N];
bool vis[N];
bool dfs0(int u, int col) {
    color[u] = col;
    for (auto v : e[u]) {
        if (v.second == -1) continue;
        if (color[v.first] != -1 and col ^ v.second != color[v.first]) return false;
        if (color[v.first] == -1 and !dfs0(v.first, col ^ v.second)) return false;
    }
    return true;
}
int dfs(int u, bool f) {
    int ans = 1;
    vis[u]  = true;
    for (auto v : e[u]) {
        if (f and v.second == -1) continue;
        if (!vis[v.first]) ans += dfs(v.first, f);
    }
    return ans;
}
void solve() {
    int n, m;
    cin >> n >> m;
    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        e[u].push_back({v, w});
        e[v].push_back({u, w});
    }
    memset(color, -1, sizeof(color));
    for (int i = 1; i <= n; ++i)
        if (color[i] == -1) {
            if (!dfs0(i, 1)) {
                cout << "0\n";
                return;
            }
        }
    int k = 0;
    for (int i = 1; i <= n; ++i)
        if (!vis[i]) k += dfs(i, false) - 1;
    memset(vis, false, sizeof vis);
    for (int i = 1; i <= n; ++i)
        if (!vis[i]) k -= dfs(i, true) - 1;
    int ans = 1;
    for (int i = 1; i <= k; ++i)
        ans *= 2, ans %= mod;
    cout << ans << '\n';
}
posted @ 2021-05-08 11:15  RioTian  阅读(109)  评论(0编辑  收藏  举报