2020牛客寒假算法基础集训营1 F maki和tree 【并查集】

题目描述

有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有 n 个顶点,n-1 条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
 和 的取法视为同一种。

输入描述 

第一行一个正整数 n 。代表顶点数量。(1\leq n\leq 100000)
第二行是一个仅由字符'B'和'W'组成的字符串。第 i 个字符是B代表第 i 个点是黑色,W代表第 i 个点是白色。
接下来的 n-1 行,每行两个正整数 x,y,代表 x 点和 y 点有一条边相连。(1\leq x,y\leq n)

输出描述

一个正整数,表示只经过一个黑色点的路径数量。

示例输入 

3
WBW
1 2
2 3

示例输出 

3

分析:

 只经过一个黑色点的路径有两种情况:

1. 黑色点在路径端点。

2. 黑色点在路径中间。

而这两种情况实际上都要求维护白色点的组,即不经过黑色点能够直接相互到达的白色点构成一组。

对于第一种情况,每个黑色点所涉及到的路径为,与该点相连的所有白点组中白点的个数之和  \small \sum_{i = 1}^{n}x_{i}

对于第二种情况,每个黑色点涉及到的路径为,与该点相连的所有白点组中,任意两个组的白点个数相乘再求和 \small \sum_{i = 1}^{n}\sum_{j = i + 1}^{n}x_{i} * x_{j}∑ i=1 n ​ x i

用并查集维护白色点的组,并维护每个组中白色点的个数。

具体解释见代码。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;

int n;

int fa[maxn],sum[maxn];
char str[maxn];
vector v[maxn];//记录与黑色点相连的白色点
vector black;//记录黑色点
map mp;

int find(int x){
	if(fa[x]==x)  return x;
	int root=find(fa[x]);
	sum[x]+=sum[fa[x]];	//路径压缩过程中,组内白点个数的更新 
	return fa[x]=root;
}

void merge(int a,int b){//两组合并
	int ffa=find(a),ffb=find(b);
    if(ffa==ffb)  return;
	fa[ffb]=ffa;
	sum[ffa]+=sum[ffb];
} 

void init(){
	for(int i=0;i<=n;i++){
		fa[i]=i;
        sum[i]=1;//初始化每个白点自己成为一组
	}
}


int main() {
    scanf("%d",&n);
    scanf("%s",str+1);
    for(int i=1;i<=n;i++){
        if(str[i]=='B'){
            mp[i]=1;
            black.push_back(i);
        }
    }
    init();
    int x,y;
    for(int i=1;i

 

你可能感兴趣的:(ACM学习,数据结构)