P3626 [APIO2009]会议中心

题目链接

首先需要知道答案的性质
如果一段区间[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;
}
发布了51 篇原创文章 · 获赞 7 · 访问量 1931

猜你喜欢

转载自blog.csdn.net/Fooooooo/article/details/105458946