题目大意:
给出 n n n个线段代表集合,现在问若可以将其中任意一个线段删除,则能够形成最多多少个独立的集合(取并集后)
解题思路1:
- 首先我们先对线段按照起点排序
- 那么我们枚举删除的线段
i
i
i,那么整个区域就切成了
[
1
,
i
−
1
]
[1,i-1]
[1,i−1]和
[
i
+
1
,
n
]
[i+1,n]
[i+1,n]的两段
- 现在我们就是要把 [ 1 , i − 1 ] [1,i-1] [1,i−1]和 [ i + 1 , n ] [i+1,n] [i+1,n]里面的线段合并就可以了
- 我们要把 [ 1 , i − 1 ] [1,i-1] [1,i−1]里面的线段算出来,并且统计出 [ 1 , i − 1 ] [1,i-1] [1,i−1]里面最远的 r r r是多大 m a x r i maxr_i maxri
- 然后我们逆着枚举线段,用单调栈去维护 [ i + 1 , n ] [i+1,n] [i+1,n]里面线段(合并之后)的左端点,然后用 m a x r i maxr_i maxri去查询里面第一个大于 m a x r i maxr_i maxri的位置就是后面的答案,就是去除重叠的部分
- 对于前面我们也可以先预处理出来每个位置线段合并的情况
Ac code 1
#include <bits/stdc++.h>
#define mid ((l + r + 1) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
PII seg[maxn];
int maxr[maxn], res[maxn];
stack<int> stk;
vector<int> cur;
int main() {
// IOS;
int T;
cin >> T;
while(T--) {
int n;
cin >> n;
for(int i = 1; i <= n; ++ i) cin >> seg[i].first >> seg[i].second;
sort(seg+1,seg+1+n);
ms(maxr,-INF);// 注意这里初始化为负数
for(int i = 2; i <= n; ++ i) maxr[i] = max(maxr[i-1],seg[i-1].second);
for(int i = 1; i <= n; ++ i) {
if(i==1) stk.push(seg[i].second);
else {
if(stk.top() >= seg[i].first) {
if(stk.top() < seg[i].second) {
stk.pop();
stk.push(seg[i].second);
}
} else stk.push(seg[i].second);
}
res[i] = stk.size();
}
cur.push_back(INF);
while(!stk.empty()) stk.pop();
int ans = 0;
for(int i = n; i >= 1; -- i) {
int l = 0, r = cur.size()-1;
while(l<r) {
if(cur[mid]<=maxr[i]) r = mid-1;
else l = mid;
}
ans = max(ans,res[i-1]+l);// 前半段+后半段答案
while(!cur.empty() && cur.back() <= seg[i].second) cur.pop_back();
cur.push_back(seg[i].first);
}
cur.clear();
cout << ans << "\n";
}
return 0;
}
解题思路2:
- 首先对于 [ a , b ] [a,b] [a,b]和 [ b + 1 , c ] [b+1,c] [b+1,c]对于这两个线段正常的扫描线对于这两种看起来是连在一起的,我们可以把区间乘以2变成 [ 2 a , 2 b ] [2a,2b] [2a,2b]和 [ 2 b + 2 , 2 c ] [2b+2,2c] [2b+2,2c],那么中间就会有空白啦 [ 2 b + 1 , 2 b + 1 ] [2b+1,2b+1] [2b+1,2b+1]
- 那么我们就把区间乘2,然后把空白区间一起放进去离散化
- 因为有的线段可以是一个点,那么我们要把扫描线线段树的叶子节点变成一个点
- 我们还要维护两个 l c o v e r lcover lcover和 r c o v e r rcover rcover,判断这个区间的左右是否被覆盖了 就这个区间合并的时候要用
- 剩下的就和扫描线差不多了
AC code2
#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = N;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
x = 0;char ch = getchar();ll f = 1;
while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
read(first);
read(args...);
}
struct Segtree {
int tr[maxn<<2];
bool led[maxn<<2];
bool red[maxn<<2];
int cover[maxn<<2];
void build(int rt, int l, int r) {
tr[rt]=led[rt]=red[rt]=cover[rt]=0;
if(l==r) return;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
void pushup(int rt) {
if(cover[rt]) tr[rt]=led[rt]=red[rt]=1;
else {
tr[rt]=tr[rt<<1]+tr[rt<<1|1];
led[rt] = led[rt<<1];
red[rt] = red[rt<<1|1];
if(red[rt<<1]&&led[rt<<1|1]) tr[rt]--;
// 如果中间连起来了线段连起来了,线段个数减1
}
}
void update(int rt, int l, int r, int posl, int posr, int val) {
if(posl<=l && posr>=r) {
cover[rt] += val;
pushup(rt);// 扫描线特性
return;
}
if(posl<=mid) update(rt<<1,l,mid,posl,posr,val);
if(posr>mid) update(rt<<1|1,mid+1,r,posl,posr,val);
pushup(rt);
}
}sgt;
vector<int> lis;
PII seg[maxn];
int n;
inline int getid(int x) {
return lower_bound(lis.begin(),lis.end(),x) - lis.begin() + 1;
}
inline void init() {
cin >> n;
for(int i = 1; i <= n; ++ i) {
int u, v;
cin >> u >> v;
seg[i] = {2*u,2*v};// 离散化
lis.push_back(2*u);
lis.push_back(2*u-1);
lis.push_back(2*v);
lis.push_back(2*v+1);
}
sort(lis.begin(),lis.end());
lis.erase(unique(lis.begin(),lis.end()),lis.end());
for(int i = 1; i <= n; ++ i) {
seg[i].first = getid(seg[i].first);
seg[i].second = getid(seg[i].second);
}
}
int main() {
IOS;
int _;
cin >> _;
while(_--) {
init();
sgt.build(1,1,lis.size());
for(int i = 1; i <= n; ++ i)
sgt.update(1,1,lis.size(),seg[i].first,seg[i].second,1);
int ans = 0;
for(int i = 1; i <= n; ++ i) {
sgt.update(1,1,lis.size(),seg[i].first,seg[i].second,-1);
ans = max(ans,sgt.tr[1]);
sgt.update(1,1,lis.size(),seg[i].first,seg[i].second,1);
}
cout << ans << "\n";
}
return 0;
}