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

1178: [Apio2009]CONVENTION会议中心

Time Limit: 15 Sec  Memory Limit: 162 MB
Submit: 812  Solved: 323
[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的整数。N≤200000

Output

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

Sample Input

4
4 9
9 11
13 19
10 17

Sample Output

2
1 3

HINT

修复数据bug,并新加数据一组By NanoApe 2016.5.11

修复后数据:JudgeOnline/upload/201605/dd.rar

Source

Solution

思路非常神

首先我们如果只要求数目最多,显然可以贪心,即线段覆盖

字典序最小就很麻烦了

方法还是贪心,按照读入的顺序,判断,如果使当前满足,能够达到最优,那么就选当前

问题是如何加速判断的过程

$f[i][j]$表示,以$i$时间开始,选$2^{j}$条线段后,右端点最近在哪

这个信息显然是可以合并的,那么倍增解决

判断的时候,用数据结构维护一下就可以,比如用set查询前驱后继

Code

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0';  ch=getchar();}
    return x*f;
}
#define MAXN 200010
#define INF 0x3f3f3f3f
int N;
struct CompanyNode{int s,t,id;}c[MAXN],tmp[MAXN],b[MAXN];
bool cmp1(CompanyNode A,CompanyNode B) {return A.s==B.s? A.t>B.t : A.s<B.s;}
bool cmp2(CompanyNode A,CompanyNode B) {return A.id<B.id;}
struct SetNode
{
    int x,f;
    SetNode (int x,int f) : x(x),f(f) {}
    bool operator < (const SetNode & A) const
        {return x<A.x;}
};
set<SetNode>st;
set<SetNode>::iterator lst,rst;
int f[MAXN<<1][20];
int Cal(int l,int r)
{
    int re=0;
    for (int i=17; i>=0; i--)
        if (f[l][i]<=r) l=f[l][i]+1,re+=1<<i;  //一定要+1
    return re;
}
int ls[MAXN<<1],tp,top;
int main()
{
    //freopen("data.in","r",stdin); freopen("data.out","w",stdout);
    N=read();
    for (int i=1; i<=N; i++) ls[++tp]=c[i].s=read(),ls[++tp]=c[i].t=read(),c[i].id=i;
    sort(ls+1,ls+tp+1);
    top=unique(ls+1,ls+tp+1)-ls-1;
    for (int i=1; i<=N; i++) 
        c[i].s=lower_bound(ls+1,ls+top+1,c[i].s)-ls,c[i].t=lower_bound(ls+1,ls+top+1,c[i].t)-ls;
    sort(c+1,c+N+1,cmp1);
    int last=INF; tp=0;
    for (int i=N; i>=1; i--) if (c[i].t<last) tmp[++tp]=c[i],last=c[i].t;
    reverse(tmp+1,tmp+tp+1);
//    for (int i=1; i<=tp; i++) printf("%d %d %d\n",tmp[i].s,tmp[i].t,tmp[i].id);
    memset(f,0x3f,sizeof(f));
//    printf("%d %d\n",f[1],INF);
    for (int i=top,j=tp; i>=1; i--)
        {
            f[i][0]=f[i+1][0];
            if (tmp[j].s==i) f[i][0]=min(f[i][0],tmp[j].t); 
            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 (tmp[j].s==i) j--;
        }
//    for (int i=1; i<=N; i++) printf("%d %d %d\n",c[i].id,c[i].s,c[i].t);
    printf("%d\n",Cal(1,top));
    sort(c+1,c+N+1,cmp2);
    st.insert(SetNode(0,-1)); st.insert(SetNode(top+1,1));
    int cnt=0;
    for (int i=1; i<=N; i++)
        {
            SetNode ls=SetNode(c[i].s,1),rs=SetNode(c[i].t,-1);
            lst=st.lower_bound(ls); rst=st.upper_bound(rs);
            if (lst!=rst || (*rst).f==-1) continue;
            lst--;
            int L=(*lst).x+1,R=(*rst).x-1;
            if (Cal(L,c[i].s-1)+Cal(c[i].t+1,R)+1==Cal(L,R))
                {
                    st.insert(ls),st.insert(rs);
                    cnt++; if (cnt==1) printf("%d",c[i].id); else printf(" %d",c[i].id);
                }
        }
    puts("");
    return 0;
}

 

转载于:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5800744.html

### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值