题目链接:传送门
题意:
一个无向图有n个节点m条边,每条边都有一个权值,然后有Q次询问,每次询问给定一个阀值,表示一个人从一个节点去另外一个节点经过的每条边的权值都不能超过这个阀值,那么这样的节点对数有多少个,(u,v)与(v,u)看作不同的。
分析:
模拟贪心法求最小生成树的过程,将所有的边按权值从小到大排序,所有的询问也按照阀值的从小到大排序,然后再边权小于阀值的时候就不断加边,然后用并查集维护一下所有联通快的数量,如果两个联通快互相不连通的话那么当他们连起来的时候所做的贡献为,他们相同后的点对数减去他们各自的点对数。
代码如下:
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> using namespace std; const int maxn = 2e4+10; struct Edge{ int u,v,w; bool operator <(const struct Edge &tmp)const{ return this->w<tmp.w; } }edg[maxn*5]; struct query{ int val,id; bool operator <(const struct query &tmp)const{ return this->val<tmp.val; } }Q[maxn/4]; struct UFS{ int par[maxn],num[maxn]; void init(){ for(int i=0;i<maxn;i++){ par[i]=i; num[i]=1; } } int find_par(int x){ if(x!=par[x]) return par[x]=find_par(par[x]); return par[x]; } void Union(int x,int y,int &sum){ x=find_par(x); y=find_par(y); if(y<x) swap(x,y); if(x!=y){ par[y]=x; sum+=(num[x]+num[y])*(num[x]+num[y]-1) - num[x]*(num[x]-1) - num[y]*(num[y]-1); num[x]+=num[y]; } } }T; int ans[maxn/4]; int main() { int t,n,m,q; scanf("%d",&t); while(t--){ scanf("%d%d%d",&n,&m,&q); for(int i=0;i<m;i++){ scanf("%d%d%d",&edg[i].u,&edg[i].v,&edg[i].w); } sort(edg,edg+m); for(int i=0;i<q;i++){ scanf("%d",&Q[i].val); Q[i].id=i; } sort(Q,Q+q); memset(ans,0,sizeof(ans)); T.init(); int sum=0; for(int i=0,j=0;i<q;i++){ while(edg[j].w<=Q[i].val&&j<m){ T.Union(edg[j].u,edg[j].v,sum); j++; } ans[Q[i].id]=sum; } for(int i=0;i<q;i++){ printf("%d\n",ans[i]); } } return 0; }