牛客练习赛142 第四次忍界大战 并查集
原题再现
链接:D-第四次忍界大战_牛客练习赛142
来源:牛客网
一棵n个结点的树,每个结点都有一个权值。
称以 u 开始、以 v 结束的树上简单路径s是好路径,当且仅当
;
- 对∀i∈(1,∣s∣),均有
,其中∣s∣表示路径中的结点个数,∣s∣≥2。
求树上好路径数量。
题目解答
题目要求的是:起终点权相等且其余点权均大于起终点权的简单路径个数。
先考虑最简单的情况,即∣s∣=2的情况。此时只需判断是否满足,如果满足路径个数+2,否则不变。这在输入边的信息时顺便处理即可。
其余情况,用到并查集和权值桶维护信息。
#include<bits/stdc++.h>
using namespace std;
long long ans;
vector<int> parent;
//查找根节点,带路径压缩
int find(int x) {if(x!=parent[x])parent[x]=find(parent[x]);return parent[x];
}
//合并两个集合
void merge(int x,int y) {int rootX=find(x);int rootY=find(y);if(rootX!=rootY)parent[rootY]=rootX;
}
signed main(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int n;cin>>n;vector<int>w(n);for(int& x:w)cin>>x;//点权map<int,vector<int>>pos;//权值桶for(int i=0;i<n;i++)pos[w[i]].push_back(i);//pos[w]是权值为w的点vector<vector<int>>adj(n);//树边for(int i=0;i<n-1;i++){int u,v;cin>>u>>v;u--,v--;adj[u].push_back(v);adj[v].push_back(u);ans+=(w[u]==w[v])*2;//|S|=2只需判断权值是否相等}//初始化并查集parent.resize(n);for(int i=0;i<n;i++)parent[i]=i;vector<int>cnt(n);vector<int>node;node.reserve(n);//从大到小遍历权值for(auto it=pos.rbegin();it!=pos.rend();it++){int val=it->first;const vector<int>&nodes=it->second;node.clear();fill(cnt.begin(),cnt.end(),0);// 处理每个节点的邻接节点for(int x:nodes){for(int y:adj[x]){if(w[y]<=val)continue;//如果邻接节点的权值小于等于当前节点,跳过int f=find(y);if(cnt[f]++==0)node.push_back(f);//如果是新加入的集合,记录下来}}// 统计集合内的路径数量for(int x:node){ans+=cnt[x]*(cnt[x]-1LL);cnt[x]=0;}// 合并集合for(int x:nodes){for(int y:adj[x]){if(w[y]>=val)merge(x,y);}}}cout<<ans;return 0;
}
参考:牛客练习赛 142 题解_ACM竞赛_ACM/CSP/ICPC/CCPC/比赛经验/题解/资讯_牛客竞赛OJ_牛客网