【BZOJ 1178】 [Apio2009]CONVENTION会议中心

1178: [Apio2009]CONVENTION会议中心

Time Limit: 15 Sec Memory Limit: 162 MB

Submit: 539 Solved: 209

[Submit][Status][Discuss]

Description

Siruseri政府建造了一座新的会议中心。许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议。 对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂。会议中心的销售主管认为:最好的策略应该是将会堂租借给尽可能多的客户。显然,有可能存在不止一种满足要求的策略。 例如下面的例子。总共有4个公司。他们对租借会堂发出了请求,并提出了他们所需占用会堂的起止日期(如下表所示)。 开始日期 结束日期 公司1 4 9 公司2 9 11 公司3 13 19 公司4 10 17 上例中,最多将会堂租借给两家公司。租借策略分别是租给公司1和公司3,或是公司2和公司3,也可以是公司1和公司4。注意会议中心一天最多租借给一个公司,所以公司1和公司2不能同时租借会议中心,因为他们在第九天重合了。 销售主管为了公平起见,决定按照如下的程序来确定选择何种租借策略:首先,将租借给客户数量最多的策略作为候选,将所有的公司按照他们发出请求的顺序编号。对于候选策略,将策略中的每家公司的编号按升序排列。最后,选出其中字典序最小1的候选策略作为最终的策略。 例中,会堂最终将被租借给公司1和公司3:3个候选策略是{(1,3),(2,3),(1,4)}。而在字典序中(1,3)<(1,4)<(2,3)。 你的任务是帮助销售主管确定应该将会堂租借给哪些公司。

Input

输入的第一行有一个整数N,表示发出租借会堂申请的公司的个数。第2到第N+1行每行有2个整数。第i+1行的整数表示第i家公司申请租借的起始和终止日期。对于每个公司的申请,起始日期为不小于1的整数,终止日期为不大于10^9的整数。

Output

输出的第一行应有一个整数M,表示最多可以租借给多少家公司。第二行应列出M个数,表示最终将会堂租借给哪些公司。

Sample Input

4

4 9

9 11

13 19

10 17

Sample Output

2

1 3

HINT

对于50%的输入,N≤3000。在所有输入中,N≤200000 请注意使用Readln读入数据……..

第一问可以直接用贪心来做,按照结束时间排序即可。

求字典序最小的解,我们按照读入顺序依次判断当前客户加入后是否可以达到最优解,如果可以达到最优解就输出这个客户编号,否则继续判断下一个。

如何快速判断加入当前的客户是否能够得到最优解?

我们找到当前要插入区间的前驱(某个客户结束时间)l和后继(某个客户的开始时间)r,然后l++,r??,找到了当前客户的插入区间。

只要计算加入这个客户之后这个区间的最优解和这个区间原本可以得到的最优解是否相等即可。

我们可以用倍增快速求出一个区间的最优解。

f[i][j]表示左端点在i,出现2j个不相交区间的最近右端点的位置,然后就可以倍增求出了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <set>
#define M 200005
#define inf 0x3f3f3f3f
using namespace std;
int f[M*2][19],l[M*2],L[M*2],N,cnt,m,n;
struct data
{
    int s,e,id;
}a[M],b[M],c[M];
struct rec
{
    int x,k;
}le,ri;
set<rec> s;
set<rec>::iterator itl,itr;
bool cmp1(data a,data b)
{
    if (a.s==b.s) return a.e>b.e;
    return a.s<b.s;
}
bool cmp2(data a,data b)
{
    return a.id<b.id;
}
bool operator <(rec a,rec b)
{
    return a.x<b.x;
}
void Prepare()
{
    sort(l+1,l+1+m);
    for (int i=1;i<=m;i++)
        if (i==1||l[i]!=l[i-1])
            L[++N]=l[i];
    for (int i=1;i<=n;i++)
    {
        a[i].s=lower_bound(L+1,L+1+N,a[i].s)-L;
        a[i].e=lower_bound(L+1,L+1+N,a[i].e)-L;
    }
    sort(a+1,a+1+n,cmp1);
    int la=inf;
    for (int i=n;i;i--)
        if (a[i].e<inf) b[++cnt]=a[i],la=a[i].e;
    for (int i=1;i<=cnt;i++)
        c[i]=b[cnt-i+1];
    memcpy(b,c,sizeof(c));
    memset(f,0x3f,sizeof(f));
    for (int i=N,j=cnt;i;i--)
    {
        f[i][0]=f[i+1][0];
        if (b[j].s==i) f[i][0]=min(f[i][0],b[j].e);
        for (int k=1;k<=17;k++)
            if (f[i][k-1]!=inf)
                f[i][k]=f[f[i][k-1]+1][k-1];
        while (b[j].s==i)
            j--;
    }
}
int Calc(int l,int r)
{
    int ans=0;
    for (int i=17;i>=0;i--)
        if (f[l][i]<=r)
            l=f[l][i],ans+=1<<i;
    return ans;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].s,&a[i].e);
        a[i].id=i;
        l[++m]=a[i].s,l[++m]=a[i].e;
    }
    Prepare();
    cout<<Calc(1,N)<<endl;
    sort(a+1,a+1+n,cmp2);
    le.x=0,le.k=2,ri.x=N+1,ri.k=1;
    s.insert(le),s.insert(ri);
    for (int i=1;i<=n;i++)
    {
        le.x=a[i].s,le.k=1,ri.x=a[i].e,ri.k=2;
        itl=s.lower_bound(le);
        itr=s.upper_bound(ri);
        if (itl!=itr||itr->k==2) continue;
        itl--;
        int ll=itl->x+1,rr=itr->x-1;
        if (Calc(ll,a[i].s-1)+Calc(a[i].e+1,rr)+1!=Calc(ll,rr))
            continue;
        s.insert(le),s.insert(ri);
        printf("%d ",a[i].id);
    }
    printf("\n");
    return 0;
}

时间: 2024-08-07 21:20:59

【BZOJ 1178】 [Apio2009]CONVENTION会议中心的相关文章

bzoj 1178 [Apio2009]CONVENTION会议中心

这题好难啊! 我好菜啊! 思路:对于最多线段不相交, 我们可以按左端点sort之后,贪心取. 但是这个题要求选取的线段排序之后序号的字典序最小. 那么我们如果按序号贪心地从大往小往里放, 那么对于第k个线段,我们考虑放进去之后是能是还能保证所取的线段个数能 达到最大, 我们考虑函数cal(l, r) 表示坐标轴上l 到 r 最多能选取多少线段,第k条线段的左右端点是l, r, 它左边第一条是l1, r1 它右边第一条是l2, r2, 那么k能放进去的充分必要条件就是cal(r1 + 1, l2

【bzoj1178】 Apio2009—CONVENTION会议中心

http://www.lydsy.com/JudgeOnline/problem.php?id=1178 (题目链接) 题意 给出n个区间,问在区间两两不相交的情况下最多能选出多少区间,并输出字典序最小的方案. Solution 考试看错题,,还有60分.. 很巧妙的一道题. 代码 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cs

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

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1178 题意概括 一堆线段,现在取出最多条数,并输出字典序最小的方案. 题解 这题好坑. 首先,注意一点,最后不能有多余的空格. 第一问就是基础的线段覆盖. 关键在于第二问. 我们要准备一个函数-- Get_Ans(L,R),用来求解L~R这个区间内,最多可以取多少线段. 这个可以O(log n)解决.前提是倍增预处理. 然后就是关键部分. 我们按照字典序从小到大,能选择就选择. 怎么判断是否可以

【BZOJ】【1178】【APIO2009】convention会议中心

贪心 如果不考虑字典序的话,直接按右端点排序,能选就选,就可以算出ans…… 但是要算一个字典序最小的解就比较蛋疼了= = Orz了zyf的题解 就是按字典序从小到大依次枚举,在不改变答案的情况下,能加进来就加. 但我想错的地方是:如果第 i 个可以在某个最优解的情况下就加入它(即判断[1,n]).但这样最后得到的可能并不是一组合法解. 所以用set维护前驱后继,判断[l,r]这一段才可以…… P.S.BZOJ200题留念 1 /*********************************

【BZOJ-1178】CONVENTION会议中心 倍增 + set (神思路好题!)

1178: [Apio2009]CONVENTION会议中心 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 812  Solved: 323[Submit][Status][Discuss] Description Siruseri政府建造了一座新的会议中心.许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议. 对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂.会议中心的销售主管认为:最好的策略应该是将会堂租借给尽

[APIO2009]会议中心

[APIO2009]会议中心 题目大意: 原网址与样例戳我! 给定n个区间,询问以下问题: 1.最多能够选择多少个不相交的区间? 2.在第一问的基础上,输出字典序最小的方案. 数据范围:\(n \leq 2\times 10^5\) 前言:几个小 \(tips\) 对于字典序最小的构造题,一个套路: 以字典序从小到大枚举,依次检查每个元素是否可以计入答案. 然后是两个函数的辨析: \(lower\_ bound(x)\):返还第一个大于等于\((\ge)\)\(x\)的位置. \(upper\_

BZOJ 1179: [Apio2009]Atm( tarjan + 最短路 )

对于一个强连通分量, 一定是整个走或者不走, 所以tarjan缩点然后跑dijkstra. --------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x)) #define foreach(i,

[Bzoj 1177][Apio2009] Oil 前缀和+递推

1177: [Apio2009]Oil Time Limit: 15 Sec Memory Limit: 162 MB Submit: 1569 Solved: 632 [Submit][Status][Discuss] Description 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整块土地为一个矩形区域,被划分为M×N个小块. Siruseri地质调查局有关于Navalur土地石油储量的估测数据.这些数据表示为M×N个非负整

bzoj 1179: [Apio2009]Atm

Description Input 第 一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路 的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就 是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,表示Banditji从市中心开始到某个酒吧结束所能抢劫的