2020年ニウケウインターホリデーアルゴリズム基礎トレーニング合宿1Fマキとツリー【要チェック】

タイトルの説明

ある日、マキは木を手に入れた。いわゆるツリーは、自己ループ、二重エッジ、ループのない無向接続グラフです。
このツリーには、n個の頂点とn-1個のエッジがあります。各頂点は白または黒に染色されます。
マキは、ブラックポイントが1つしかない単純なパスでいくつの異なるポイントが取得されるのか知りたいですか?
注:
①ツリー上の2つの単純なパスは、2つのポイントを結ぶ最短パスを指します。
②<p、q>と<q、p>の方法は同じとみなす。

説明を入力してください 

1行目の正の整数n。頂点の数を表します。(1 \ leq n \ leq 100000)
2行目は、文字「B」と「W」のみで構成される文字列です。i番目の文字はBで、i番目の点は黒で、Wは白です。
次のn-1行、2つの正の整数x、yの各行は、xとyがエッジで接続されていることを表します。(1 \ leq x、y \ leq n)

出力の説明

黒い点のみを通過するパスの数を示す正の整数。

入力例 

3
WBW
1 2
2 3

出力例 

分析:

 1つの黒い点のみを通過するパスには、次の2つの場合があります。

1.黒い点はパスの最後にあります。

2.黒い点はパスの中央にあります。

どちらの場合も、実際には白いドットのグループ、つまり黒いドットを通過せずに互いに直接到達できる白いドットのグループを維持する必要があります。

最初のケースでは、各黒い点に含まれるパスは、その点に接続されているすべての白い点グループの白い点の数の合計   \ small \ sum_ {i = 1} ^ {n} x_ {i}です。

2番目のケースの場合、各ブラックポイントに含まれるパスは、ポイントに接続されているすべてのホワイトポイントグループで、2つのグループのホワイトポイントの数が乗算され、合計されます。 \ small \ sum_ {i = 1} ^ {n} \ sum_ {j = i + 1} ^ {n} x_ {i} * x_ {j} ∑ i = 1 n xi

白い点を維持するグループを使用して確認し、各グループの白い点の数を維持します。

具体的な説明については、コードを参照してください。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;

int n;

int fa[maxn],sum[maxn];
char str[maxn];
vector<int> v[maxn];//记录与黑色点相连的白色点
vector<int> black;//记录黑色点
map<int,int> 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<n;i++){
        scanf("%d%d",&x,&y);
        if(mp[x]&&!mp[y]){
            v[x].push_back(y);
        }
        else if(!mp[x]&&mp[y]){
            v[y].push_back(x);
        }
        else if(!mp[x]&&!mp[y]){//两个白点相连,合并两组
            merge(x,y);
        }
    }
    ll ans=0;
    for(int i=0;i<black.size();i++){//对每个黑点进行计算
        ll cnt=0;//记录所有与该黑点相连组的白点总数,简化第二种情况下的内层循环
        for(int j=0;j<v[black[i]].size();j++){
            int ff1=find(v[black[i]][j]);
            ans=ans+sum[ff1];
            cnt+=sum[ff1];
        }
        for(int j=0;j<v[black[i]].size();j++){
            int ff1=find(v[black[i]][j]);
            cnt-=sum[ff1];
            ans=ans+cnt*sum[ff1];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

公開された30元の記事 ウォンの賞賛5 ビュー900

おすすめ

転載: blog.csdn.net/qq_42840665/article/details/104210347