题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4750
题意:给出一个无向图,f(a,b)表示从点a到点b的所有路径中的每条路径的最长边中的最小值,给出 p个询问,每个询问有一个数t,对于每个询问,求有多少对顶点f(a,b)小于t。注意(1,2)和(2,1)是不同的点对
分析:
正过来想不太好做..反过来..看在当前t的限制下..有多少个点对f(u,v)<t...这样答案就是totol-sum...totol是总对数n*(n-1)...sum是当前可联通的..
这样转化后就不难想到将所有的询问从小到大排序..将所有的边从小到大排序..然后根据递增的询问不断地加边统计答案..而要统计答案和维护图的联通关系时用到并查集....如此时间复杂度为O(m).有点像模拟最小生成树的Kurskal
对于每一条边(a,b),判断两个端点a,b是否属于同一个集合,如果不是,则当前边就是a,b所有路径中要求的“瓶颈边”(因为所有的边是从小到大排序的),这时小于t的点对数为sum加上这两个集合的顶点点个数乘积的两倍,即sum+=num[f1]*num[f2]*2,最后对于每个询问输出total-sum即可。
AC代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 struct NODE 6 { 7 int u,v,w; 8 bool operator <(const NODE &a)const{ 9 return w<a.w; 10 } 11 }node[500005]; 12 struct T 13 { 14 int d,id; 15 bool operator <(const T &a)const{ 16 return d<a.d; 17 } 18 }t[100005]; 19 int num[10005],fa[10005],ans[100005]; 20 int find(int x) 21 { 22 if(x!=fa[x]) 23 fa[x]=find(fa[x]); 24 return fa[x]; 25 } 26 int main() 27 { 28 int n,m,j,i,p; 29 while(scanf("%d%d",&n,&m)!=EOF) 30 { 31 for(i=0;i<m;i++) 32 scanf("%d%d%d",&node[i].u,&node[i].v,&node[i].w); 33 scanf("%d",&p); 34 for(i=0;i<p;i++) 35 { 36 scanf("%d",&t[i].d); 37 t[i].id=i; 38 } 39 sort(node,node+m); 40 sort(t,t+p); 41 for(i=0;i<n;i++) 42 { 43 fa[i]=i; 44 num[i]=1; 45 } 46 int total=n*(n-1); 47 int sum=0; 48 j=0; 49 for(i=0;i<p;i++) 50 { 51 while(j<m&&node[j].w<t[i].d) 52 { 53 int f1=find(node[j].u); 54 int f2=find(node[j].v); 55 if(f1==f2) 56 { 57 j++; 58 continue; 59 } 60 sum+=num[f1]*num[f2]*2; 61 fa[f1]=f2; 62 num[f2]+=num[f1]; 63 j++; 64 } 65 ans[t[i].id]=total-sum; 66 } 67 for(i=0;i<p;i++) 68 printf("%d ",ans[i]); 69 } 70 return 0; 71 }