#P1537. 2023.09.03-第3题-小红的红白色块
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 158
            Accepted: 31
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              字节
                                
            
                        
              时间 :2023年9月3日
                              
                      
          
 
- 
                        算法标签>并查集          
 
2023.09.03-第3题-小红的红白色块
思路:并查集
给定一个无向图,有些节点是白色,有些节点是红色。求将第i个节点染红后,有多少个红色连通块。
红色节点染红,显然红色连通块的数目不变。
将白色节点染红,则要取决于与该染红节点相邻的节点有多少已经是红色的。而这些已经是红色的节点有可能已经位于同一个连通块中了,因此我们需要使用并查集来判断它们是否处于同一个集合。
最开始,通过某一个未被标记的红色节点开始遍历,将其所能到达的所有红色节点划分到该集合中,然后标记。重复上述过程,直到所有红色节点都被划分进集合中。此时可以求到集合总数为res,也就是最开始的答案或者说将红色节点染红的答案。
然后开始求取答案。
由于有了并查集,当我们将一个白色节点染红时,可以遍历所有与相邻的节点,计算有多少个集合,假设为x,那么最终的答案即为res−x+1
代码
C++
AC
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
char c[maxn];
int head[maxn], ver[maxn << 1], Next[maxn << 1], tot = 1;
int fa[maxn];
bool book[maxn];
// 添加一条边
void add(int x, int y) {
    ver[++tot] = y;
    Next[tot] = head[x];
    head[x] = tot;
}
// 查找父节点
int find(int x) {
    return fa[x] = (x == fa[x] ? x : find(fa[x]));
}
// 合并两个集合
void merge(int x, int y) {
    int fax = find(x);
    int fay = find(y);
    if (fax != fay) fa[fax] = fay;
}
// 深度优先搜索,建立红色连通分量
void dfs0(int x) {
    book[x] = true;
    for (int i = head[x], y; i; i = Next[i]) {
        y = ver[i];
        if (c[y] == 'R' && !book[y]) {
            merge(x, y);
            dfs0(y);
        }
    }
}
// 计算某个节点的红色连通分量数量
int solve(int x) {
    memset(book, 0, sizeof(book));
    int cnt = 0;
    int fax = find(x);
    for (int i = head[x], y; i; i = Next[i]) {
        y = ver[i];
        if (c[y] == 'R' && !book[y]) {
            if (book[find(y)] == false) {
                book[find(y)] = true;
                cnt++;
            }
            book[y] = true;
        }
    }
    return cnt;
}
int main() {
    cin >> n >> m;
    scanf("%s", c + 1);
    // 构建图
    for (int i = 1, x, y; i <= m; ++i) {
        cin >> x >> y;
        add(x, y);
        add(y, x);
    }
    for (int i = 1; i <= n; ++i) {
        fa[i] = i;
    }
    int res = 0;
    // 找到红色连通分量数量
    for (int i = 1; i <= n; ++i) {
        if (!book[i] && c[i] == 'R') {
            dfs0(i);
            res++;
        }
    }
    for (int i = 1; i <= n; ++i) {
        if (c[i] == 'R') {
            printf("%d\n", res);
        } else {
            printf("%d\n", res - solve(i) + 1);
        }
    }
    return 0;
}
python
n, m = map(int, input().split())
c = ' '+input().strip()
maxn = 10 ** 5 + 10
head = [0] * (maxn)
ver = [0] * (maxn << 1)
Next = [0] * (maxn << 1)
tot = 1
fa = [0] * (maxn)
book = [False] * (maxn)
# 添加一条边
def add(x, y):
    global tot
    tot += 1
    ver[tot] = y
    Next[tot] = head[x]
    head[x] = tot
# 查找父节点
def find(x):
    if x == fa[x]:
        return x
    fa[x] = find(fa[x])
    return fa[x]
# 合并两个集合
def merge(x, y):
    fax = find(x)
    fay = find(y)
    if fax != fay:
        fa[fax] = fay
# 深度优先搜索,建立红色连通分量
def dfs0(x):
    book[x] = True
    i = head[x]
    while i!=0:
        y = ver[i]
        if c[y] == 'R' and not book[y]:
            merge(x, y)
            dfs0(y)
        i = Next[i]
# 计算某个节点的红色连通分量数量
def solve(x):
    global book
    book = [False] * (maxn)
    cnt = 0
    fax = find(x)
    i = head[x]
    while i != 0:
        y = ver[i]
        if c[y] == 'R' and not book[y]:
            if not book[find(y)]:
                book[find(y)] = True
                cnt += 1
            book[y] = True
        i = Next[i]
    return cnt
# 构建图
for _ in range(m):
    x, y = map(int, input().split())
    add(x, y)
    add(y, x)
for i in range(1, n + 1):
    fa[i] = i
res = 0
# 找到红色连通分量数量
for i in range(1, n + 1):
    if not book[i] and c[i] == 'R':
        dfs0(i)
        res += 1
for i in range(1, n + 1):
    if c[i] == 'R':
        print(res)
    else:
        print(res - solve(i) + 1)
Java
import java.util.*;
public class Main {
    static int maxn = 100_010;
    static int maxm = 2 * maxn;
    static int[] head = new int[maxn];
    static int[] ver = new int[maxm];
    static int[] Next = new int[maxm];
    static int[] fa = new int[maxn];
    static boolean[] book = new boolean[maxn];
    static int tot = 1;
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        scanner.nextLine(); // Consume the newline character after reading m
        String c = " " + scanner.nextLine().trim();
        for (int i = 1; i <= n; i++) {
            head[i] = 0;
            fa[i] = i;
            book[i] = false;
        }
        for (int i = 0; i < m; i++) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            add(x, y);
            add(y, x);
        }
        int res = 0;
        for (int i = 1; i <= n; i++) {
            if (!book[i] && c.charAt(i) == 'R') {
                dfs0(i, c);
                res += 1;
            }
        }
        for (int i = 1; i <= n; i++) {
            if (c.charAt(i) == 'R') {
                System.out.println(res);
            } else {
                System.out.println(res - solve(i, c) + 1);
            }
        }
    }
    static void add(int x, int y) {
        ver[tot] = y;
        Next[tot] = head[x];
        head[x] = tot;
        tot++;
    }
    static int find(int x) {
        if (x == fa[x]) {
            return x;
        }
        fa[x] = find(fa[x]);
        return fa[x];
    }
    static void merge(int x, int y) {
        int fax = find(x);
        int fay = find(y);
        if (fax != fay) {
            fa[fax] = fay;
        }
    }
    static void dfs0(int x, String c) {
        book[x] = true;
        int i = head[x];
        while (i != 0) {
            int y = ver[i];
            if (c.charAt(y) == 'R' && !book[y]) {
                merge(x, y);
                dfs0(y, c);
            }
            i = Next[i];
        }
    }
    static int solve(int x, String c) {
        Arrays.fill(book, false);
        int cnt = 0;
        int fax = find(x);
        int i = head[x];
        while (i != 0) {
            int y = ver[i];
            if (c.charAt(y) == 'R' && !book[y]) {
                if (!book[find(y)]) {
                    book[find(y)] = true;
                    cnt += 1;
                }
                book[y] = true;
            }
            i = Next[i];
        }
        return cnt;
    }
}
        题目描述
小红有一个无向图,图中有若干个节点是红色,其余为白色。小红想知道,将i染成红色,当前图中红色连通块的数量是多少?
输入描述
第一行输入两个正整数n,m,代表无向图的节点数和边数。
第二行输入一个长度为n的字符串,第i个字符为"R"代表节点被染成红色,"W"代表节点被染成白色。
接下来的m行,每行输入两个正整数u,v,代表节点u和节点v有一条无向边连接。
请注意,无向图不保证是连通的,而且可能有重边和自环。
1≤n,m≤105
1≤u,v≤n
输出描述
一共n行,第i行代表将i染红,当前的红色连通块数量
样例
输入
4 4
WRWW
1 2
2 3
1 3
1 4
输出
1
1
1
2