CodeCraft-22 and Codeforces Round #795 (Div. 2) E(并查集+扫描线)
CodeCraft-22 and Codeforces Round #795 (Div. 2) E(并查集+扫描线)
题意:
给 \(n\) 个线段,线段要么是红色要么蓝色。现在要对线段进行合并。只有有区间重叠的才能合并,合并要求红区间只能直接的并入蓝区间,蓝区间只能直接的并入红区间。问最后会分成多少堆。
思路:
感觉上是想办法用并查集维护这个合并过程。
因为有颜色限制,肯定不能直接写。先来分析这干了什么事。
考虑一个线段 \([l,r]\),这个线段可以合并所有异色有重合的线段。这些异色的线段可以开两个集合存,当这个线段结束,就从集合中弹出。
但此时还有一个问题就是,对 集合中的一条线段很可能出现多次合并的情况,所以肯定要把重复操作剪掉。还是和并查集类似的思想,用一个线段表示整个集合,这里需要使用最靠右的线段表示整个集合。
最后就是代码实现上,用的是扫描线的思想,把线段拆成两个事件,一个活动,一个结束,从左往右扫,遇到活动的就将之放入集合,做并查集的合并,遇到结束,就把线段从集合中删除。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<string>
#include<random>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
struct Node {
int id,col,p,t;
};
int R[MAXN];
int fa[MAXN];
struct Cmp {
bool operator() (const int a,const int b) const{
if(R[a] != R[b]) return R[a] < R[b];
return a<b;//一定要判R[a] == R[b] 不然会错
}
};
int root(int x) {
return fa[x] == x ? x : (fa[x] = root(fa[x]));
}
void unite(int x,int y) {
x = root(x) , y = root(y);
if(x == y) return;
fa[x] = y;
}
void solve()
{
int n; cin >> n;
vector<Node> a;
rep(i,1,n) {
fa[i] = i;
int c,l,r; cin >> c >> l >> r;
R[i] = r;
a.push_back({i,c,l,0});
a.push_back({i,c,r,1});
}
sort(a.begin(),a.end(),[&](Node x,Node y) {
if(x.p != y.p) return x.p < y.p;
return x.t < y.t;
});
int m = a.size();
set<int,Cmp> s0,s1;
rep(i,0,m - 1) {
auto [id,col,p,t] = a[i];
// cout << id << " " << p << " " << (t ? 'r' : 'l') << endl;
if(t == 0) {
if(!col) {
s0.insert(id);
while(s1.size() > 1) {
unite(*s1.begin(), id);
s1.erase(s1.begin());
}
if(!s1.empty()) unite(*s1.begin(), id);
}else {
s1.insert(id);
while(s0.size() > 1) {
unite(*s0.begin(), id);
s0.erase(s0.begin());
}
if(!s0.empty()) unite(*s0.begin(),id);
}
}else {
if(!col) {
s0.erase(id);
}else {
s1.erase(id);
}
}
}
vector<int> sz(n + 1);
rep(i,1,n) sz[root(i)] ++;
int ans = 0;
rep(i,1,n) ans += (sz[i] != 0);
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
solve();
return 0;
}