hdu3938(离线并查集)

xiaoxiao2021-02-27  153

好题!!!

题意:给你一个能量,问你能这些能量能修多少种路,修一条路所需要的能量是这条路的起点和终点所有路径中最长的一条边的最小值。

既然是最长边的最小值,那么确定一条边以后后来出现的在路径上的长的边就没有用了。所以可以按照克鲁斯卡尔的思想对边进行排序。

用并查集判环(类似最小生成树),当新加入一条边时,以这条边为花费的点对就是两个未合并的并查集里面的点的数量的乘积。然后用前缀和保存答案。

因为边的长度为1E8,所以要先把问题保存起来,再去算(我的理解是 类似离散化)。

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,m,q; int par[10005]; struct node { int from; int to; int w; bool operator < (const node& T) const { return w<T.w; } }edge[50004]; int sum[10005]; struct answer { int l; int pos; int ans ; bool operator < (const answer& T) const { return l<T.l; } }query[10005]; int find(int u) { if(u==par[u]) return u; return par[u] = find(par[u]); } int unite(int u,int v) { int fa = find(u); int fb = find(v); if(fa==fb) return 0; int tmp=0; if(fa<fb) { par[fb] = fa; tmp = sum[fa]*sum[fb]; sum[fa] += sum[fb]; } else { par[fa] = fb; tmp = sum[fa] * sum[fb]; sum[fb] += sum[fa]; } return tmp; } bool cmp(answer a,answer b) { return a.pos<b.pos; } int main() { int u,v,w; while(scanf("%d%d%d",&n,&m,&q)!=EOF) { for(int i=0;i<=n;i++) par[i] = i,sum[i] = 1; for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].from ,&edge[i].to,&edge[i].w); sort(edge,edge+m); for(int i=0;i<q;i++) { scanf("%d",&query[i].l); query[i].pos = i; query[i].ans = 0; } int cnt = 0; sort(query,query+q); for(int i=0;i<q;i++) { while(edge[cnt].w<=query[i].l&&cnt<m) { int fa = find(edge[cnt].from); int fb = find(edge[cnt].to); if(fa==fb) { cnt++; continue; } else { query[i].ans += unite(edge[cnt].from,edge[cnt].to); cnt++; } } if(i>=1) query[i].ans += query[i-1].ans; } sort(query,query+q,cmp); for(int i=0;i<q;i++) { printf("%d\n",query[i].ans); } } return 0; }

转载请注明原文地址: https://www.6miu.com/read-16901.html

最新回复(0)