Codeforces 1156D 0-1-Tree ( 并查集 || 树形DP )

<题目链接>

题目大意:

给定一颗无向树,树的边权只要0/1两种情况,现在问你这棵树上存在多少对有序对<u,v>,满足u-->v的路径上,如果出现边权为1的边之后,就不能出现边权为0的边,问你这样的有序对的个数。

解题分析:

本题可以用并查集和树形DP来求解。

并查集就是用两类并查集来分别维护每个点所在的连通块0和连通块1的信息(连通块0表示之间的点都是通过边权为0的边相连接,连通块1类似)。

然后对于每个点,考虑三种情况:

1)该点所在的连通块0的所有节点相互之间自行匹配,假设该点所在连通块0点数为sz,有序对数就是有向完全图的有向边边数$(sz*(sz-1))$(这种情况每个连通块0只需要考虑一次);

2)对于每个点所在连通块1的情况考虑,与上面类似;

3)每个点作为所在连通块0和1的交叉点,情况数就是 $(sz0-1)*(sz1-1)$。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 2e5+5;
ll sz[N][2];
int n,fa[N][2];
//第二维表示是第几类连通块,0表示边权为0的联通块,1表示边权为1的
int find(int u,int x){    return u==fa[u][x]?u:fa[u][x]=find(fa[u][x],x); }

inline void Union(int u,int v,int x){
    int f1=find(u,x),f2=find(v,x);
    if(f1!=f2){
        fa[f2][x]=f1;
        sz[f1][x]+=sz[f2][x];     //记录连通块中的节点个数
    }
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        sz[i][0]=1,sz[i][1]=1;
        fa[i][0]=i,fa[i][1]=i;
    }
    for(int i=1;i<n;i++){
        int u,v,w;cin>>u>>v>>w;
        Union(u,v,w);       //将对应分类的连通块合并
    }
    ll ans=0;
    for(int i=1;i<=n;i++){
        if(fa[i][0]==i)ans+=sz[i][0]*(sz[i][0]-1);     //讨论块内的有序对,有序对数就是有向完全图的边数  
        if(fa[i][1]==i)ans+=sz[i][1]*(sz[i][1]-1);
        int f0=find(i,0),f1=find(i,1);
        ans+=(sz[f0][0]-1)*(sz[f1][1]-1)*1LL;       //然后将每个点作为两类连通块的交叉点的情况考虑一下
    }cout<<ans<<endl;
}

树形DP做法待补:

原文地址:https://www.cnblogs.com/00isok/p/10802286.html