<题目链接>
题目大意:
给定一颗无向树,树的边权只要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做法待补: