题意:给n个节点m条带权值边的无向图。然后q个问题,每次询问点对的数目,点对需要满足的条件是:1)连通;2)其路径的最大权值不能超过询问值。
分析:如果没次询问一次,dfs一次,很可能超时,因此可以用并查集。离线处理,把边按权值排序,把问题按大小排序。然后离线的过程就是不断向图中加边的过程。
比如样例如下:
然后离线处理,排完序后将会是一条一条的加边:问题也排了序,因此是个累加过程。。。
1 #include <cstdio> 2 #include <iostream> 3 #include <sstream> 4 #include <cmath> 5 #include <cstring> 6 #include <cstdlib> 7 #include <string> 8 #include <vector> 9 #include <map> 10 #include <set> 11 #include <queue> 12 #include <stack> 13 #include <algorithm> 14 #define ll long long 15 #define mem(m, a) memset(m, a, sizeof(m)) 16 #define repu(i, a, b) for(int i = a; i < b; i++) 17 #define maxn 100005 18 const double PI=-acos(-1.0); 19 using namespace std; 20 struct node 21 { 22 int x,y,z; 23 bool operator < (const node &a) const 24 { 25 return z < a.z; 26 } 27 } e[maxn]; 28 struct Q 29 { 30 int w,id; 31 bool operator < (const Q &a) const 32 { 33 return w < a.w; 34 } 35 } q[maxn]; 36 int ans[maxn]; 37 int fa[maxn],num[maxn]; 38 int find_set(int x) 39 { 40 if(x==fa[x]) 41 return fa[x]; 42 else 43 return fa[x]=find_set(fa[x]); 44 } 45 int main() 46 { 47 int n,m,k,t; 48 scanf("%d",&t); 49 while(t--) 50 { 51 scanf("%d%d%d",&n,&m,&k); 52 repu(i,0,m) 53 scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z); 54 repu(i,0,k) 55 scanf("%d",&q[i].w),q[i].id=i; 56 sort(e,e+m); 57 sort(q,q+k); 58 repu(i,1,1+n) 59 fa[i]=i,num[i]=1; 60 int cnt=0,j=0; 61 repu(i,0,k) 62 { 63 while(j<m&&e[j].z<=q[i].w) 64 { 65 int x=find_set(e[j].x); 66 int y=find_set(e[j].y);///找各自的所属的集合编号 67 if(x != y) 68 { 69 cnt += num[x]*num[y];///各自集合的数目相乘 70 fa[x] = y;///因为已经统一集合了 71 num[y] += num[x];///y集合的数目就可以直接加上x的数目 72 } 73 j++; 74 } 75 ans[q[i].id] = 2*cnt; 76 } 77 repu(i,0,k) 78 printf("%d\n",ans[i]); 79 } 80 return 0; 81 }
时间: 2024-12-25 19:22:10