搜了题解才把题搞明白。明白之后发现其实题意很清晰,解题思路也很清晰,只是题目表述的很不清晰……
大意如下——
给你一个无向图,图中任意两点的距离是两点间所有路径上的某一条边,这条边需要满足两个条件:1. 这条边这两点间某条路径上的最长边;2. 这条边是这两点间所有路径上的最长边中的最短边。
简单来说,假如a到d有两条路径,一条经过b,一条经过d,其中ab = 1, bd = 3, ac = 2, cd = 2,那么abd上的最长边为3,acd上的最长边为2,则ad的距离为2。
如果a, d两点间的距离小于能量L,那么就可以在a, d两点间建立一个传送门。
现在,求在L的能量下最多可以在这个图中建立多少个传送门。
输入:
多组输入数据。
每组输入数据第一行包括三个整数n, m, q。表示节点数,边数,请求数。
接下来m行,每行三个整数u, v, val,表示边的源点,目的点,边权(注意,是无向图,源点和目的点等价)。
接下来q行,每行一个整数L,表示请求所提供的能量。
解题核心:如果集合x与集合y不连通,而此时有一条路L'将x与y连通,且L' <= L,此时将可以建立新传送门num[x]*num[y]个,num[x]表示x集合中的节点数。L1连通后,将集合x与集合y合并,得到新集合x,num[x] += num[y],这就是并查集。
可以使用并查集+kruskal进行求解。即,将所有边从小到大排序,每次按顺序向并查集中增加新边,需要保证添加的新边不会构成环,直到边长>请求所提供的能量L。
新问题出现了,当我们在L1的能量下将路径求出来了,那么如果下一次请求能量为L2,那么我们无法在已有的并查集上继续求解,只能重新建立并查集,这将产生极大的浪费。所以,我们需要将请求L1——Lq全部记录下来,即离线操作,然后按照从小到大的顺序进行求解。最后在将解按照请求顺序排序输出。
上代码——
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 7 const int M = 10010; 8 9 struct Que //保存查询 10 { 11 int q, id, ans; //分别是查询值,查询顺序,输出结果 12 }que[M]; 13 14 struct Edge //保存边 15 { 16 int u, v, val; 17 }edge[5*M]; 18 19 int fm[M]; //并查集使用 20 int sum[M]; //记录各区间节点数 21 22 int n, m, q; 23 24 bool cmp(Edge x, Edge y) 25 { 26 return x.val <= y.val; 27 } 28 29 bool cmp1(Que x, Que y) 30 { 31 return x.q <= y.q; 32 } 33 34 bool cmp2(Que x, Que y) 35 { 36 return x.id < y.id; 37 } 38 39 void init() 40 { 41 for(int i = 1; i <= n; i++) 42 { 43 fm[i] = i; 44 sum[i] = 1; 45 } 46 for(int i = 0; i < m; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].val); 47 sort(edge, edge+m, cmp); //按路径长度从小到大排序 48 49 for(int i = 0; i < q; i++) 50 { 51 scanf("%d", &que[i].q); 52 que[i].id = i; 53 que[i].ans = 0; 54 } 55 sort(que, que+q, cmp1); //按请求长度从小到大排序 56 } 57 58 int mfind(int x) //查询操作,含路径压缩 59 { 60 int fx = x; 61 while(fx != fm[fx]) fx = fm[fx]; 62 while(x != fm[x]) 63 { 64 int mid = fm[x]; 65 fm[x] = fx; 66 x = mid; 67 } 68 return fx; 69 } 70 71 void work() 72 { 73 int cnt = 0; 74 for(int i = 0; i < q; i++) //回应请求 75 { 76 while(que[i].q >= edge[cnt].val && cnt < m) //kruskal算法 77 { 78 int fx = mfind(edge[cnt].u); 79 int fy = mfind(edge[cnt].v); 80 if(fx != fy) 81 { 82 que[i].ans += sum[fx]*sum[fy]; //新增传送阵 83 fm[fy] = fx; //集合合并 84 sum[fx] += sum[fy]; 85 } 86 cnt++; 87 } 88 if(i > 0) que[i].ans += que[i-1].ans; //包含已有传送阵 89 } 90 } 91 92 void output() 93 { 94 sort(que, que+q, cmp2); //按请求顺序排序 95 for(int i = 0; i < q; i++) printf("%d ", que[i].ans); 96 } 97 98 int main() 99 { 100 //freopen("test.txt", "r", stdin); 101 while(~scanf("%d%d%d", &n, &m, &q)) 102 { 103 init(); 104 work(); 105 output(); 106 } 107 return 0; 108 }