题面:https://codeforces.com/contest/1691/problem/E
这道题真的几把坑。
解题思路:
有几组连在一起,所以很显然会想到并查集。
假设我们现在先不看颜色,那很自然的人类的想法, 就是从左到右找有没有断点。
这样可以判断就几组,但是无法判断连的关系,所以思考连在一起的关系,就是
- a.l<=b.l
- a.r>=b.l
所有我们只要给2n个点(左端点和右端点)拍个序,扫过去,如果是左端点就存进来,如果是右端点就将对应的组删去。假如我们走到pos点且这个点是左端点,这时候有5个组还在里面,那个这个组和这5个组就是相连的。(理解一下上面两个式子这就很自然了)
不过注意,排序的时候因为点可能重合,所有如果点的pos都一样,得先放左端点。
理解了连的关系,接下来看怎么连,如果和每个都连,那就是n2,所以得用并查集连他们的爹。
哪个能代表这5个组的爹呢,我们要的爹是可以代表这么多个点,而且考虑这5个点+新进来的点有消失/都是没消失的情况下,又新进来个左端点, 那么这时候左端点就是只要连这个爹。(假设在5个点之前,我们还没有采用连爹的策略,这一步我们聪明了)没消失,连那个都行;如果消失了一个,那是不是连4+1都行,如果消失了两个,那只能连3+1……所以很简单,爹得保证儿子全死了,自己还可以被连,所以就是最晚死的那个,也就是右端点最远的那个组。
现在我们考虑颜色,还是刚才的5个的情况(这5个同色),假设来个不同颜色的,我就得连这5个,接下来再来一个不同颜色的, 那我就只需要连5个里面右端点最远的那个。
这时候就有个问题,为啥第一个得连5个,第二个只用连一个。
实际上是我们知道了5是爹,所以第二个只要连他就行。所以怎么知道5是爹?很简单,把他儿子全sha了就行。
大致过程就是这样,维护两个有序数组表示两个颜色。
有一个无敌坑的点:如果你打算用set做的话,结构体重载的时候,记得把结构体所有的东西最好都比比,即使他没啥意义,不然就会出现erase({2,3}),但是把{2,4}给删了 (set查你要找的东西,是a不大于b,b不大于a,就认为他们相等)
(因为思路改了几次,所以定义的很乱,见谅)
代码实现:
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#define DEBUG(x) cout<<"** "<<x<<" **"<<endl;
using namespace std;
typedef long long ll;
struct node{
int pos;
int color;
int lr;
int num;
};
struct point{
int color;
int l;
int r;
}pp[200005];
struct Right{
int num;
int r;
friend bool operator < (Right a,Right b){
if(a.r!=b.r)return a.r>b.r;
else return a.num<b.num;
}
};
int n;
bool cmp(node a,node b){
if(a.pos!=b.pos){
return a.pos<b.pos;
}else{
return a.lr<b.lr;
}
}
int fa[200005];
//int vis[200005];
void init(){
for(int i=1;i<=n;i++){
fa[i]=i;
// vis[i]=0;
}
}
int find(int a){
if(fa[a]==a){
return a;
}
return fa[a]=find(fa[a]);
}
void connect(int a,int b){
a=find(a);
b=find(b);
if(a!=b){
fa[a]=b;
}
}
void dd(){
for(int i=1;i<=n;i++){
printf("%d $\t",find(i));
}
printf("\n");
}
int main(){
int t;
cin>>t;
int a,b,c;
while(t--){
cin>>n;
vector<node> p;
for(int i=1;i<=n;i++){
scanf("%d %d %d",&a,&b,&c);
p.push_back({b,a,0,i});
p.push_back({c,a,1,i});
pp[i]={a,b,c};
}
init();
// dd();
sort(p.begin(),p.end(),cmp);
set<Right> st0;
set<Right> st1;
for(node tmp:p){
if(tmp.lr==1){
if(tmp.color==0){
st0.erase({tmp.num,tmp.pos});
}else{
st1.erase({tmp.num,tmp.pos});
}
continue;
}
if(tmp.color==0){
st0.insert({tmp.num,pp[tmp.num].r});
if(!st1.empty()){
Right ttt=(*st1.begin());
for(auto tt:st1){
connect(tt.num,tmp.num);
}
st1.clear();
st1.insert(ttt);
}
}else{
st1.insert({tmp.num,pp[tmp.num].r});
if(!st0.empty()){
Right ttt=(*st0.begin());
for(auto tt:st0){
connect(tt.num,tmp.num);
}
st0.clear();
st0.insert(ttt);
}
}
// dd();
}
set<int> c;
for(int i=1;i<=n;i++){
c.insert(find(i));
}
cout<<c.size()<<endl;
}
return 0;
}