题目链接:点击这里
思路:我们可以发现带有黑色节点的路径无非只有两种情况:
- 黑色节点在路径的中间
- 黑色节点在路径的端点
因此我们可以先预处理,将每个白点连通块上的白点个数统计出来,这样,我们就可以得知每个黑点所连接的白点的权值(即连通块白点数)。
设某黑点连接了
个白点,第
个白点的权值为
。
这样第一种路径的数量为
,第二种路径的数量为
。
第一种可以用前缀和处理达成 计算。
#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;
}