首先需要知道答案的性质
如果一段区间[L->R]可以成为答案,那么他在答案中左面最近区间的右边界r和右面最近区间左边界l之间满足
r+1->l-1之间的最大区间数=r+1->L-1之间的最大区间数+R+1->l-1之间的最大区间数+1
证明 :反证法,如果枚举的这一段区间里有2个区间的话,r+1->l-1之间的最大区间数=r+1->L-1之间的最大区间数+R+1->l-1之间的最大区间数+2
我们知道这个性质之后 只需要按字典序枚举每条边判断是否可以加入答案就好了
因此问题就转化为如何求一段区间内的最大区间数
仅仅是对这个问题的话,很明显的 如果有一段大区间里包含一段小区间,为了最优答案那么大区间就可以舍去。
因此我们建立了在a数组的基础上建立t数组。
使用倍增即可在O(logn)时间内求出一段区间内最大的区间数(本质上是贪心思想,因为筛选出来的区间已经按右边界排序)
根据第二问的解法,那么第一问就不需要另外的做法了,直接求-INF~INF之间的最大区间数即可
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
#define N 200010
#define INF 0x3f3f3f3f
int n,m;
int f[N][25];
int X[N],Y[N];
struct node {
int l,r;
bool operator <(const node &a)const{
return r==a.r?l>a.l:r<a.r;
}
}t[N],a[N];
set<node>s;
int calc(int l,int r){
int x=lower_bound(X+1,X+1+m,l)-X;
if(x>m||Y[x]>r) return 0;
int ans=1;
for(int i=20;i>=0;i--)
if(f[x][i]&&Y[f[x][i]]<=r) ans+=1<<i,x=f[x][i];
return ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>t[i].l>>t[i].r,a[i]=t[i];
sort(t+1,t+1+n);
m=0;
for(int i=1;i<=n;i++)
if(t[i].l>t[m].l) t[++m]=t[i];
for(int i=1;i<=m;i++) X[i]=t[i].l,Y[i]=t[i].r;
for(int i=1,j=1;i<=m;i++){
while(j<=m&&t[j].l<=t[i].r) j++;
if(j<=m) f[i][0]=j;
}
for(int i=m;i>0;i--)
for(int j=1;j<=20;j++)
f[i][j]=f[f[i][j-1]][j-1];
cout<<calc(-INF,INF)<<endl;
s.insert({-INF,-INF});s.insert({INF,INF});
for(int i=1;i<=n;i++){
auto x=s.lower_bound(a[i]),y=x;y--;
if(x->l<=a[i].r||y->r>=a[i].l) continue;
if(calc(y->r+1,x->l-1)==calc(y->r+1,a[i].l-1)+calc(a[i].r+1,x->l-1)+1)
cout<<i<<' ',s.insert(a[i]);
}
return 0;
}