2022.10.14 总结

1. 逐月 P8006

逐月 P8006

题意#

Qn 颗珍珠,每颗珍珠可以是红色或者蓝色。

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

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

思路#

100 分#

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

先介绍一下二分图:

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

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

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

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

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

先说结论,令 cnt 为连通块数量,那么方案数就是 2cnt

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

所以方案数是 2cnt

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

时间复杂度#

图的遍历,点数 + 边数,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;
}

作者:cn

出处:https://www.cnblogs.com/chengning0909/p/17173890.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   chengning0909  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示