[BZOJ1178][Apio2009]CONVENTION会议中心(单调栈+dp+set)

题目描述

传送门

题目大意:有一些线段,两两之间不能相交,求:最短选出多少条线段;输出所有可选的方案中字典序最小的。

题解

这题并没有做出来。。。

这道题的关键在于一个性质:令get_ans(l,r)表示在区间[l,r]内最多能选出多少条合法的线段,那么如果现在要判断线段[l0,r0]是否能加入,就是要判断是否get_ans(l,r)=get_ans(l,l0-1)+get_ans(r0+1,r)+1,其中l和r为已插入的线段中,l0左边最近的右端点和r0右边最近的左端点
这个式子很容易理解,其实就是判断是否有合法的方案
那么如何求出get_ans(l,r)呢?首先我们将相互包含的线段中大的线段去掉,容易知道这一定是更优的,这一步可以用单调栈解决;令f(i,j)表示从第i条线段开始向右一共选了 2j 条合法的线段最近到达的线段的编号,由于所有的线段没有包含关系,这个数组可以通过倍增求出;然后对于get_ans(l,r),我们只需要从[l,r]范围内的第一条线段开始蹦,每一次蹦一个最大的2的幂次,知道蹦不了或者蹦出[l,r]的范围就行了,一次查询的复杂度是 O(log2n)
那么按照编号从小到大枚举每一条线段,首先判断其和之前选择过的线段是否相交;如果不相交,则判断是否满足上面的表达式,然后再插入即可
判断是否和之前选择过的线段相交比较麻烦,具体做法是:看l前的第一个是否是线段的左端点,不合法;看r后面的第一个是否是线段的右端点,不合法;看[l,r]中是否有其他线段的端点
这些找前驱和后继的都用set来实现就可以了

好神的思路啊。

代码

#include
#include
#include
#include
#include
#include
using namespace std;
#define N 200005
#define sz 18
#define inf 1000000000

int n,cnt,f[N][sz+3],stack[N],seg[N],sol[N];
struct data{int x,y,id;}a[N],b[N];
set < pair<int,int> > lr,rr;

int cmp(data a,data b){return a.xint cmpid(data a,data b){return a.idvoid Unique()
{
    sort(b+1,b+n+1,cmp);
    int top=0;
    for (int i=1;i<=n;++i)
    {
        if (b[i].x==b[stack[top]].x&&b[i].y>=b[stack[top]].y) continue;
        while (b[i].y<=b[stack[top]].y)
            --top;
        stack[++top]=i;
    }
    n=top;
    for (int i=1;i<=n;++i)
        a[i]=b[stack[i]];
}
int find(int lim)
{
    int l=1,r=n,mid,ans=-1;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (a[mid].x>=lim) ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int get_ans(int l,int r)
{
    int idl=find(l);
    if (idl==-1) return 0;
    int ans=0;
    for (int i=sz-1;i>=0;--i)
        if (f[idl][i]!=-1&&a[f[idl][i]].y<=r)
        {
            ans+=1<1);
            if (idl==-1) break;
        }
    return ans;
}
int main()
{
    scanf("%d",&n);cnt=n;
    for (int i=1;i<=n;++i) scanf("%d%d",&b[i].x,&b[i].y),b[i].id=i;
    Unique();
    memset(f,-1,sizeof(f));

    for (int i=1;i<=n;++i)
    {
        lr.insert(make_pair(a[i].x,i));
        f[i][0]=i;
    }
    lr.insert(make_pair(inf+1,0));
    for (int j=1;jfor (int i=1;i<=n;++i)
            if (i+(j<<1)-1<=n)
            {
                if (f[i][j-1]==-1) continue;
                int loc=(*lr.upper_bound(make_pair(a[f[i][j-1]].y,inf))).second;
                if (!loc||f[loc][j-1]==-1) continue;
                f[i][j]=f[loc][j-1];
            }

    lr.clear();rr.clear();
    lr.insert(make_pair(0,0));lr.insert(make_pair(inf+1,0));
    rr.insert(make_pair(0,0));rr.insert(make_pair(inf+1,0));
    sort(b+1,b+cnt+1,cmpid);
    for (int i=1;i<=cnt;++i)
    {
        int p,q; 
        p=(*--rr.upper_bound(make_pair(b[i].x,0))).first; 
        q=(*--lr.upper_bound(make_pair(b[i].x,0))).first;
        if ((p||q)&&q>p) continue;
        p=(*rr.lower_bound(make_pair(b[i].y,0))).first;
        q=(*lr.lower_bound(make_pair(b[i].y,0))).first;
        if ((p!=inf+1||q!=inf+1)&&pcontinue;

        p=(*rr.lower_bound(make_pair(b[i].x,0))).first;
        if (p<=b[i].y) continue;
        p=(*lr.lower_bound(make_pair(b[i].x,0))).first;
        if (p<=b[i].y) continue;
        p=(*--rr.upper_bound(make_pair(b[i].y,0))).first;
        if (p>=b[i].x) continue;
        p=(*--lr.upper_bound(make_pair(b[i].x,0))).first;
        if (p>=b[i].x) continue;

        int l=(*--rr.lower_bound(make_pair(b[i].x,0))).first;
        int r=(*lr.lower_bound(make_pair(b[i].y,0))).first;
        if (get_ans(l,r)==get_ans(l,b[i].x-1)+get_ans(b[i].y+1,r)+1)
        {
            sol[++sol[0]]=b[i].id;
            lr.insert(make_pair(b[i].x,b[i].id));
            rr.insert(make_pair(b[i].y,b[i].id));
        }
    }
    printf("%d\n",sol[0]);
    for (int i=1;i<=sol[0];++i)
    {
        if (i!=sol[0]) printf("%d ",sol[i]);
        else printf("%d",sol[i]);
    }
}

你可能感兴趣的:(题解,dp,单调栈,stl)