zoukankan      html  css  js  c++  java
  • 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做法待补:

  • 相关阅读:
    985的方格难题
    POJ 3264 区间最大最小值Sparse_Table算法
    oracle中to_date详细用法示例(oracle日期格式转换)
    PLSQL基础知识-图片
    oracle-查询-时间条件查询
    oracle基础函数--decode
    PLSQL基础学习-文字
    python3 MD5
    CentOS7关闭防火墙方法
    CentOS 7下源码安装MySQL 5.6
  • 原文地址:https://www.cnblogs.com/00isok/p/10802286.html
Copyright © 2011-2022 走看看