题目描述
在Byteland一共有n个城市,编号依次为1到n,它们之间计划修建m条双向道路,其中修建第i条道路的费用为ci。
Byteasar作为Byteland公路建设项目的总工程师,他决定选定一个区间[l,r],仅使用编号在该区间内的道路。他希望选择一些道路去修建,使得连通块的个数尽量少,同时,他不喜欢修建多余的道路,因此每个连通块都可以看成一棵树的结构。
为了选出最佳的区间,Byteasar会不断选择q个区间,请写一个程序,帮助Byteasar计算每个区间内修建公路的最小总费用。
数据分析
分析
为什么数组开小了是WA不是RE,害我调了一个晚上啊
题目中是区间查询,所以很容易想到用线段树维护答案
显然,在子区间内不会构成最小生成树的边,在父区间内一定不在最小生成树上
所以每个区间内只需要从小到大保存该区间范围内所有边中构成最小生成树/森林的边
在区间合并时,运用归并的思想依次取子区间中较小边做 $Kruskal$
时间复杂度为 $O((m+q \; log \; m)n \alpha (n))$
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <queue> using namespace std; #define ll long long #define inf 0x3f3f3f3f #define N 105 #define M 100005 #define lc (p << 1) #define rc (p << 1 | 1) #define mid (l + r >> 1) int n, m, q; int f[N], tmp[N << 1]; struct Edge {int u, v, w;} edge[M]; struct Tree {int sum, len, e[N];} t[M << 2]; int find(int x) { if (x == f[x]) return x; return f[x] = find(f[x]); } Tree merge(Tree l, Tree r) { Tree p; p.sum = p.len = 0; int i = 1, j = 1, k = 0; while (i <= l.len && j <= r.len) { if (edge[l.e[i]].w < edge[r.e[j]].w) tmp[++k] = l.e[i++]; else tmp[++k] = r.e[j++]; } while (i <= l.len) tmp[++k] = l.e[i++]; while (j <= r.len) tmp[++k] = r.e[j++]; for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= k; i++) { int x = find(edge[tmp[i]].u); int y = find(edge[tmp[i]].v); if (x == y) continue; f[x] = y; p.e[++p.len] = tmp[i]; p.sum += edge[tmp[i]].w; } return p; } void build(int p, int l, int r) { if (l == r) { t[p].e[t[p].len = 1] = l; t[p].sum = edge[l].w; return; } build(lc, l, mid); build(rc, mid + 1, r); t[p] = merge(t[lc], t[rc]); } Tree query(int p, int l, int r, int ql, int qr) { if (l == ql && r == qr) return t[p]; if (qr <= mid) return query(lc, l, mid, ql, qr); if (ql > mid) return query(rc, mid + 1, r, ql, qr); return merge(query(lc, l, mid, ql, mid), query(rc, mid + 1, r, mid + 1, qr)); } int main() { scanf("%d%d%d", &n, &m, &q); for (int i = 1; i <= m; i++) scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].w); build(1, 1, m); while (q--) { int l, r; scanf("%d%d", &l, &r); printf("%d\n", query(1, 1, m, l, r).sum); } return 0; }