BZOJ1178 [Apio2009]CONVENTION会议中心 贪心 set

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1178


题意概括

  一堆线段,现在取出最多条数,使其互不覆盖,并输出字典序最小的方案。


 

题解

  这题好坑。

  首先,注意一点,最后不能有多余的空格。

  第一问就是基础的线段覆盖。

  关键在于第二问。

  我们要准备一个函数—— Get_Ans(L,R),用来求解L~R这个区间内,最多可以取多少线段。

  这个可以O(log n)解决。前提是倍增预处理。

  然后就是关键部分。

  我们按照字典序从小到大,能选择就选择。

  怎么判断是否可以选择?只需要在之前选择的线段空隙中可以放这个线段,并且满足Get_Ans(这个线段到它左边的第一条线段之前)+Get_Ans(它到它右边的第一个线段之前)+1 = Get_Ans(他左右两端的),那么就可以放进去。


代码

 

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <set>
using namespace std;
const int N=200000+5,Inf=1e9;
struct seg{
    int L,R;
    bool operator < (const seg x) const {
        if (L==x.L)
            return R>x.R;
        return L<x.L;
    }
}s[N],s2[N];
int n,rn,Hash[N*2],hs,p[N][20],L[N];
bool alive[N],vis[N*2];
set <int> S;
void HASH(){
    sort(Hash+1,Hash+hs+1);
    int hs_=1;
    for (int i=2;i<=hs;i++)
        if (Hash[i]!=Hash[i-1])
            Hash[++hs_]=Hash[i];
    hs=hs_;
}
void Delete(){
    for (int i=1;i<=n;i++)
        s2[i]=s[i];
    sort(s+1,s+n+1);
    memset(alive,true,sizeof alive);
    int MinR=Inf,n_=0;
    for (int i=n;i>=1;i--)
        if (MinR>s[i].R)
            MinR=s[i].R;
        else
            alive[i]=0;
    for (int i=1;i<=n;i++)
        if (alive[i])
            s[++n_]=s[i];
    n=n_;
}
void Get_Pos(){
    memset(p,0,sizeof p);
    for (int i=1;i<=n;i++)
        L[i]=s[i].L;
    L[n+1]=Inf;
    for (int i=1;i<=n;i++){
        p[i][0]=upper_bound(L+1,L+n+2,s[i].R)-L;
        if (p[i][0]==n+1)
            p[i][0]=0;
    }
    for (int i=1;i<=18;i++)
        for (int j=1;j<=n;j++)
            p[j][i]=p[p[j][i-1]][i-1];
}
int Get_Ans(int Le,int Ri){
    if (Le>Ri||Le>hs)
        return 0;
    int ans=1,pos=lower_bound(L+1,L+n+2,Le)-L;
    if (pos>n||Ri<s[pos].R)
        return 0;
    for (int i=18;i>=0;i--){
        if (!p[pos][i]||s[p[pos][i]].R>Ri)
            continue;
        pos=p[pos][i];
        ans+=1<<i;
    }
    return ans;
}
int main(){
    scanf("%d",&n),rn=n;
    for (int i=1;i<=n;i++){
        scanf("%d%d",&s[i].L,&s[i].R);
        Hash[++hs]=s[i].L;
        Hash[++hs]=s[i].R;
    }
    HASH();
    for (int i=1;i<=n;i++){
        s[i].L=lower_bound(Hash+1,Hash+hs+1,s[i].L)-Hash;
        s[i].R=lower_bound(Hash+1,Hash+hs+1,s[i].R)-Hash;
    }
    Delete();
    Get_Pos();
    printf("%d\n",Get_Ans(1,hs));
    S.clear();
    S.insert(Inf),S.insert(-Inf);
    bool empty_block=0;
    for (int i=1;i<=rn;i++){
        int now=*S.lower_bound(s2[i].L);
        if (now==Inf||!vis[now]){
            if (now<=s2[i].R)
                continue;
            int left=*--S.lower_bound(s2[i].L);
            int right=*S.lower_bound(s2[i].R);
            int val=Get_Ans(left+1,s2[i].L-1)+1+Get_Ans(s2[i].R+1,right-1);
            if (val<Get_Ans(left+1,right-1))
                continue;
            S.insert(s2[i].L);
            S.insert(s2[i].R);
            if (s2[i].L!=s2[i].R)
                vis[s2[i].R]=1;
            if (empty_block)
                putchar(' ');
            printf("%d",i);
            empty_block=1;
        }
    }
    return 0;
}

 

  

 

posted @ 2017-08-18 18:18  zzd233  阅读(427)  评论(0编辑  收藏  举报