牛客 maki和tree(思维、并查集)

题目链接:点击这里

在这里插入图片描述
在这里插入图片描述
思路:我们可以发现带有黑色节点的路径无非只有两种情况:

  1. 黑色节点在路径的中间
  2. 黑色节点在路径的端点

因此我们可以先预处理,将每个白点连通块上的白点个数统计出来,这样,我们就可以得知每个黑点所连接的白点的权值(即连通块白点数)。

设某黑点连接了 k k 个白点,第 i i 个白点的权值为 f ( i ) f(i)
这样第一种路径的数量为 i = 1 k j = i + 1 k f ( i ) f ( j ) \mathop{ \sum }\limits_{{i=1}}^{{k}}{{\mathop{ \sum }\limits_{{j=i+1}}^{{k}}{f \left( i \left) *f \left( j \right) \right. \right. }}} ,第二种路径的数量为 i = 1 k f ( i ) {\mathop{ \sum }\limits_{{i=1}}^{{k}}{f \left( i \right) }}

第一种可以用前缀和处理达成 O ( k ) O(k) 计算。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<map>
#include<set>

using namespace std;
typedef long long ll;
const int MOD = 10000007;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const int maxn = 1e5+10;

vector<int> G[maxn];
char str[maxn];
int n, f[maxn], num[maxn];
ll tmp[maxn], sum[maxn];

int Find(int x)					//查找 
{
    if(x!=f[x])
		f[x] = Find(f[x]);		//路径压缩
    return f[x];
}

void Union(int x, int y)		//合并 
{
	x = Find(x);
	y = Find(y);
	if(x!=y)
		f[x] = y;
}

void count()				//统计每个元素所在集合的元素个数
{
	for(int i = 1; i <= n; ++i)
	{
		f[i] = Find(i);		//寻找每个节点的父节点
		num[f[i]]++;		//统计父节点下的节点个数
	}
	
	for(int i = 1; i <= n; ++i)		//统计父节点外的点的个数
		num[i] = num[f[i]];
}


int main()
{
	scanf("%d", &n);
	scanf("%s", str+1);
	
	for(int i = 1; i <= n; ++i)
		f[i] = i;
	
	for(int i = 1; i < n; ++i)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		G[x].push_back(y);				//建无向图 
		G[y].push_back(x);
		if(str[x]=='W'&&str[y]=='W')	//合并白色连通块
			Union(x,y);
	}
	
	count();
	
	ll ans = 0;
	for(int i = 1; i <= n; ++i)
	{
		if(str[i]=='B')
		{
			memset(tmp, 0, sizeof(tmp));
			memset(sum, 0, sizeof(sum));
			int cnt = 0;
            for(int j = 0; j < G[i].size(); ++j)
            	if(str[G[i][j]]=='W')
            		tmp[++cnt] = num[G[i][j]];		//若相邻点是白点,加入temp
			
			for(int j = 1; j <= cnt; ++j)
				sum[j] = sum[j-1] + tmp[j];
			
//			printf("%d\n", sum[cnt]);				//sum[cnt]是第二种路径的数量 
			
			ll res = 0;
			for(int j = cnt; j >= 2; --j)
				res += tmp[j]*sum[j-1];
			
//			printf("%d\n", res);					//res是第一种路径的数量 
			
            ans += (res+sum[cnt]);
		}
	}
	
	printf("%lld\n", ans);
	return 0;
}
发布了727 篇原创文章 · 获赞 111 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_42815188/article/details/104220706