E. Turtle and Intersected Segments
题意
有
n
n
n 个区间,第
i
i
i 个区间是
[
l
i
,
r
i
]
[l_i, r_i]
[li,ri],权值为
a
i
a_i
ai
两个区间相交当且仅当:
max
(
l
1
,
l
2
)
≤
min
(
r
1
,
r
2
)
\max(l_1, l_2) \leq \min(r_1,r_2)
max(l1,l2)≤min(r1,r2)
对于两两相交的区间,在它们之间连边,权值为:
∣
a
i
−
a
j
∣
|a_i - a_j|
∣ai−aj∣
问最小生成树边权值之和为多少(或不存在)?
思路
首先,对于三条两两相交的线段
(
l
1
,
r
1
,
a
1
)
,
(
l
2
,
r
2
,
a
2
)
,
(
l
3
,
r
3
,
a
3
)
(l_1, r_1, a_1), (l_2, r_2, a_2), (l_3, r_3, a_3)
(l1,r1,a1),(l2,r2,a2),(l3,r3,a3),不妨设
a
1
<
a
2
<
a
3
a_1 < a_2 < a_3
a1<a2<a3,不难发现:我们只需要保留
(
1
,
2
)
,
(
2
,
3
)
(1, 2), (2, 3)
(1,2),(2,3) 之间的边即可。
这是由于
a
3
−
a
1
=
a
3
−
a
2
+
a
2
−
a
1
a_3 - a_1 = a_3 - a_2 + a_2 - a_1
a3−a1=a3−a2+a2−a1,而在生成树中,原图中的每一个环,边权最大的那一条边一定不会出现在
M
S
T
MST
MST 中
因此我们可以用扫描线活动来解决这个流程,每条线段在 l l l 处加入,在 r + 1 r + 1 r+1 处删除。加入扫描线数组时,按照 ( l , i ) , ( r + 1 , − i ) (l, i), (r + 1, -i) (l,i),(r+1,−i) 加入并排序,这样子的好处是,在同一个位置,删除线段的活动总是优先于加入线段的活动,因为 s e c o n d second second 为负。
每次加入一条新的线段时,此时还未删除的线段一定都与其相交(它们的
l
<
l
i
,
r
≥
l
i
l < l_i, r \geq l_i
l<li,r≥li)
我们只需要找到其按
a
i
a_i
ai 排序的前驱和后继线段,连边即可。
这样子连边的数量是 O ( n ) O(n) O(n) 的,总时间复杂度为: O ( n log n ) O(n \log n) O(nlogn)
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
struct node{
int l;
int r;
int w;
};
struct Edge{
int u;
int v;
int w;
};
const int N = 500005;
int fa[N];
std::vector<Edge> edges;
int find(int x){
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void solve(){
int n;
std::cin >> n;
std::vector<node> a(n + 1);
fore(i, 1, n + 1) std::cin >> a[i].l >> a[i].r >> a[i].w;
std::vector<std::pair<int, int>> b;
fore(i, 1, n + 1){
b.push_back({a[i].l, i});
b.push_back({a[i].r + 1, -i});
}
std::sort(ALL(b));
edges.clear();
std::set<std::pair<int, int>> st;
for(auto [p, i] : b){
if(i < 0){
st.erase({a[-i].w, -i});
}
else{
auto it = st.lower_bound({a[i].w, 0});
if(it != st.end()) edges.push_back({i, it -> se, it -> fi - a[i].w});
if(it != st.begin()){
--it;
edges.push_back({i, it -> se, a[i].w - it -> fi});
}
st.insert({a[i].w, i});
}
}
std::sort(ALL(edges), [](const Edge& lhs, const Edge& rhs){
return lhs.w < rhs.w;
});
fore(i, 1, n + 1) fa[i] = i;
ll ans = 0;
int cnt = 0;
for(auto [u, v, w] : edges){
int fau = find(u), fav = find(v);
if(fau == fav) continue;
ans += w;
fa[fau] = fav;
++cnt;
if(cnt == n - 1) break;
}
std::cout << (cnt == n - 1 ? ans : -1) << endl;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin >> t;
while(t--){
solve();
}
return 0;
}