关键是利用“所有从s到t的路径上的最长边的最小值”这个条件,我们所要的只是这个最小值:
对于(s,t),这个最小值一定是s到t的最短路上的最长边,则所有可能被取到的边是最小生成树上的边
因为最小生成树利用的就是贪心,树上的边都是图上每两点间的最短路会经过的边
实现:
在kruskal的过程中,添加一条边,权值为w,两个连通分量u和v变为连通,有num[u]*num[v]*2对pair会取到这个w,因为(s,t)和(t,s)不同所以*2
统计,对于一个f,对所有t<=f的guests更新
#include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <algorithm> using namespace std; #define N 10010 #define M 500500 struct Edge { int u,v,w; }edge[M]; bool cmp(Edge u,Edge v) { return u.w<v.w; } struct Q { int t,idx,val; Q(int t=0,int idx=0,int val=0) { this->t=t;this->idx=idx;this->val=val; } }q[N*14]; bool cmp2(Q u,Q v) { return u.t<v.t; } int n,m,p,fa[N],num[N],ans[N*10]; int _find(int u) { return fa[u]==u?u:fa[u]=_find(fa[u]); } void update(int f,int val) { int left=0,right=p,mid; while(left<right) { mid=left+right>>1; if(q[mid].t<=f) left=mid+1; else right=mid; } if(q[left].val>f) return; q[0].val+=val; q[left].val-=val; } int main () { while(scanf("%d%d",&n,&m)!=EOF) { for(int i=0;i<m;++i) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); scanf("%d",&p); for(int i=0;i<p;++i) { scanf("%d",&q[i].t); q[i].idx=i;q[i].val=0; } sort(q,q+p,cmp2); sort(edge,edge+m,cmp); for(int i=0;i<n;++i) fa[i]=i,num[i]=1; int cc=0; for(int i=0;i<m;++i) { int u=_find(edge[i].u); int v=_find(edge[i].v); if(u==v) continue; update(edge[i].w,num[u]*num[v]*2); fa[u]=v; num[v]+=num[u]; if(++cc>=n-1) break; } for(int i=0,k=0;i<p;++i) { k+=q[i].val; q[i].val=k; } for(int i=0;i<p;++i) ans[q[i].idx]=q[i].val; for(int i=0;i<p;++i) printf("%d\n",ans[i]); } return 0; }