Loading

2022.10.14 总结

1. 逐月 P8006

逐月 P8006

题意

\(Q\)\(n\) 颗珍珠,每颗珍珠可以是红色或者蓝色。

\(L\) 会问 \(m\) 个问题,每个问题都是:编号为 \(x\) 的珍珠和编号为 \(y\) 的珍珠颜色是否相同。如果答案为 \(0\),则说明不同,如果答案为 \(1\),则说明一样。

现在小 \(L\) 想知道小 \(Q\) 是否一定说了谎,如果不一定,他还想知道有多少种可行的方案。

思路

100 分

我们可以将珍珠视为点,问题视为边,所以,这个题目就变成了一道模板的 二分图染色 了。

先介绍一下二分图:

对于每一个点,都可以染成黑色或者白色,存在一种方案,使得任意相邻两个点的颜色都不同。

所以,这就是一个图的深搜(耶!)

那么,什么时候这个图是不合法的呢?

就是,当这个点已经被遍历过了,并且,它的颜色和它应该是的颜色不同,就说明,这个图是不合法的。

但是,要怎么求方案数呢?

先说结论,令 \(cnt\) 为连通块数量,那么方案数就是 \(2 ^ {cnt}\)

因为,对于每个连通块,可以把它们分成两个部分,第一个部分的点颜色为 \(1\),第二个部分的点颜色为 \(2\)\(1\)\(2\) 又不可以相等,所以每个连通块都有两种涂色方法。

所以方案数是 \(2 ^ {cnt}\)

然后,事情就变得简单了,直接做图的遍历即可。

时间复杂度

图的遍历,点数 + 边数,\(O(n + m)\)

空间复杂度

邻接表,\(O(m)\)

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 300010, MOD = 998244353;

int n, m, tp, x, y, w, cnt, v[N];
bool flag = 1;

struct C{
  int d, w;
};

vector<C> f[N];

void dfs(int t, int co){   // 二分图染色
  if (v[t] != -1) {
    flag &= (v[t] == co);
    return ;
  }
  v[t] = co;
  for (int i = 0; i < f[t].size(); i++) {
    dfs(f[t][i].d, f[t][i].w ^ co);
  }
}

int main(){
  freopen("pearl.in", "r", stdin);
  freopen("pearl.out", "w", stdout);
  cin >> n >> m >> tp;
  fill(v + 1, v + n + 1, -1);
  for (int i = 1; i <= m; i++) {
    cin >> x >> y >> w;
    f[x].push_back({y, !w}), f[y].push_back({x, !w});  // 邻接表
  }
  for (int i = 1; i <= n; i++) {
    if (v[i] == -1) {
      cnt++;
      dfs(i, 0);   // 搜索连通块
    }
  }
  if (!flag) {
    if (tp == 1) {
      cout << "NO";
    } else {
      cout << 0;
    }
  } else {
    if (tp == 1) {
      cout << "YES";
    } else {
      long long ans = 1;
      for (int i = 1; i <= cnt; i++) {
        ans = ans * 2 % MOD;
      }
      cout << ans;
    }
  }
  return 0;
}
posted @ 2023-03-02 22:42  chengning0909  阅读(11)  评论(0编辑  收藏  举报