这是一道好题,让我又学了一个新的知识,离线算法+并查集
题意:先给出图,求存在多少路径使得花费T小于L,T的定义是u,v亮点的所有路径的最大边的最小值
(Unfortunately, making a pair of portals will cost min{T} energies. T in a path between point V and point U is the length of the longest edge in the path)
分析:首先将需要询问的Q进行排序,从小到大,因为L的值大的必定包含了L值小的路径。然后将两点各自的集合相乘num[u]*num[v],可以细想一下,假设与u已经合并的点有2个,与v合并的点有3个,那么u,v两集合所能组成的点就是2*3,因为两集合当前存储的边必定都小于T,而且必定都小于T,然后再讲u,v两集合合并,形成新的集合num[u]。对于边的查找,只需要找到小于等于T就可以停止查找,我们可以看一下,当u,v边长为L‘加入,且L'>L,若u,v在一个集合,则新通路为0,因为这两点的路径个数在以前加过了,若u,v在两个集合,则它们之间只有一条通路,可想而知,这条通路的权值为L‘,则无论组成的哪天连通两点的路径其最大边都为L’。
#include<stdio.h> #include<algorithm> using namespace std; const int MAXN= 50010; struct Edge { int a,b,l; } edge[MAXN]; struct Node { int t,pos; } pl[MAXN/5]; int father[MAXN/5],num[MAXN/5]; long long temp[MAXN/5]; bool cmp1(Edge a,Edge b) { return a.l<b.l; } bool cmp2(Node a,Node b) { return a.t<b.t; } void Make_set(int n) { for (int i=1; i<=n; i++) { father[i]=i; num[i]=1; } } int Find(int x) { int r=x; while(r!=father[r])//这里写错了。。 { r=father[r]; } if(r!=x) father[x]=r; return father[x]; } int Union(int s1,int s2) { int x=Find(s1); int y=Find(s2); if(x==y) return 0; long long t=num[x]*num[y]; num[x]+=num[y];//祖先代表该集合的总点数 num[y]=0;//当成为一个集合只需要有个祖先就够了,其他的点为了避免重复均=0 father[y]=x; return t; } int main() { int n,m,q,i; while(scanf("%d%d%d",&n,&m,&q)!=EOF) { Make_set(n); for(i=0; i<m; i++) { scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].l); } sort(edge,edge+m,cmp1); for(int k=0; k<q; k++) { scanf("%d",&pl[k].t); pl[k].pos=k; } sort(pl,pl+q,cmp2); long long ans=0; int pos=0; for(i=0; i<q; i++) { while(pos<m && edge[pos].l<=pl[i].t) { ans+=Union(edge[pos].a,edge[pos].b); pos++; } temp[pl[i].pos]=ans; } for(i=0; i<q; i++) { printf("%lld\n",temp[i]); } } return 0; }