Problem Description
给出 (n) 个线段 ([l_1,r_1],[l_2,r_2],cdots,[l_n,r_n]),求最少去掉多少个线段才能存在一个线段 ([l_k,r_k]) 使得它与其余所有线段重叠。
(1le nle 2 imes 10^5),(1le lle rle 10 imes 9)。
Solution
枚举哪个线段与其他所有线段重合,并通过树状数组计算与之重合的线段的数量,数量最多的即为所求线段。
至于如何统计,可以将所有线段以左端点为第一关键字、右端点为第二关键字排序,对于每一个线段 ([l_i,r_i]),左端点小于它的(([l_1,r_1],[l_2,r_2],cdots,[l_{i-1},r_{i-1}])),统计右端点处于区间 ([l_i,+infty)) 的有多少个;左端点大于它的(([l_{i+1},r_{i+1}],[l_{i+2},r_{i+2}],cdots,[l_n,r_n])),统计左端点处于区间 ([l_i,r_i]),二者相加的和即与该线段重合的线段的数量。由于 (1 leq l leq r leq 10^9),树状数组统计前需要离散化。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int N=2e5;
struct segment
{
int l,r;
segment() {}
bool operator < (const segment &x) const {return l==x.l?r<x.r:l<x.l;}
}a[N+10];
int c1[N*2+10],c2[N*2+10],n,m,t[N*2+10];
void modify(int *c,int x,int d) {for(;x<=m;x+=x&-x) c[x]+=d;}
int query(int *c,int x)
{
int ans=0;
for(;x;x-=x&-x) ans+=c[x];
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&a[i].l,&a[i].r);
t[i*2-1]=a[i].l;
t[i*2]=a[i].r;
}
sort(a+1,a+n+1);
sort(t+1,t+n*2+1);
m=unique(t+1,t+n*2+1)-t-1;
for(int i=0;i<=m;i++)
{
c1[i]=0;
c2[i]=0;
}
for(int i=1;i<=n;i++)
{
a[i].l=lower_bound(t+1,t+m+1,a[i].l)-t;
a[i].r=lower_bound(t+1,t+m+1,a[i].r)-t;
}
int ans=0;
for(int i=1;i<=n;i++) modify(c2,a[i].l,1);
for(int i=1;i<=n;i++)
{
ans=max(ans,query(c1,m)-query(c1,a[i].l-1)+query(c2,a[i].r)-query(c2,a[i].l-1));
modify(c2,a[i].l,-1);
modify(c1,a[i].r,1);
}
printf("%d
",n-ans);
}
return 0;
}