CodeForces 1156 D.0-1-Tree(并查集)

D.0-1-Tree

You are given a tree (an undirected connected acyclic graph) consisting of n vertices and n−1 edges. A number is written on each edge, each number is either 0 (let’s call such edges 0-edges) or 1 (those are 1-edges).

Let’s call an ordered pair of vertices (x,y) (x≠y) valid if, while traversing the simple path from x to y, we never go through a 0-edge after going through a 1-edge. Your task is to calculate the number of valid pairs in the tree.

Input

The first line contains one integer n (2≤n≤200000) — the number of vertices in the tree.

Then n−1 lines follow, each denoting an edge of the tree. Each edge is represented by three integers xi, yi and ci (1≤xi,yi≤n, 0≤ci≤1, xi≠yi) — the vertices connected by this edge and the number written on it, respectively.

It is guaranteed that the given edges form a tree.

Output

Print one integer — the number of valid pairs of vertices.

Example
Input

7
2 1 1
3 2 0
4 2 1
5 2 0
6 7 1
7 2 1

Output

34

思路:

首先只走白色边和只走黑色边的点对合法
因此开两个并查集单独合并白边连通块和黑边连通块

对于每个同色的连通块
答案累加num*(num-1)

题目说走1边之后不能走0边,意思是可以走0边之后走1边,
对于每个点i,这该点属于的0连通块大小为num0,1连通块大小为1
则0的连通块走到1的连通块方案数为(num0-1)*(num1-1),累加至答案
减1是因为要去掉自己

code:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define int long long
const int maxm=2e5+5;
int c[maxm],cc[maxm];//两种颜色的连通块大小
int f[maxm],ff[maxm];//两个并查集的pre数组
void init(){
    for(int i=0;i<maxm;i++){
        f[i]=ff[i]=i;
        c[i]=cc[i]=1;
    }
}
int ffind(int x,int *pre){
    return pre[x]==x?x:pre[x]=ffind(pre[x],pre);
}
void add(int a,int b,int ccc){//按颜色合并
    if(ccc==1){//黑色
        int x=ffind(a,f);
        int y=ffind(b,f);
        if(x!=y){
            f[x]=y;
            c[y]+=c[x];
        }
    }else{//白色
        int x=ffind(a,ff);
        int y=ffind(b,ff);
        if(x!=y){
            ff[x]=y;
            cc[y]+=cc[x];
        }
    }
}
signed main(){
    init();
    int n;
    cin>>n;
    for(int i=1;i<n;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(f[i]==i){//如果是黑根
            ans+=c[i]*(c[i]-1);//1->1
        }
        if(ff[i]==i){//如果是白根
            ans+=cc[i]*(cc[i]-1);//0->0
        }
        int x=ffind(i,f);//当前点属于的连通块的黑根
        int y=ffind(i,ff);//当前点属于的连通块的白根
        ans+=(c[x]-1)*(cc[y]-1);//0->1
    }
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值