HDU 5441 离线处理 + 并查集
题意:给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 }
人生就像心电图,想要一帆风顺,除非game-over