• BZOJ1178 [Apio2009]CONVENTION会议中心


     

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

    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

    正解:倍增+贪心

    解题报告:  

       题目要求从若干个闭区间中取出尽可能多的区间,并且保证方案的字典序最小。

       在只有第一问的情况下,这就是一道贪心的经典模型了。但是我们考虑如何保证我们选出来的方案字典序最小。显然我们可以发现一个问题,如果我们从第一个区间开始考虑,假设把它选了,没有使总数量减少,那么我们肯定可以选择它(通过字典序最小的原则很容易想通)。那么我们的任务就是如何快速的检查是否不会使得总数量减少,所以我们需要做的就是快速查询区间的最大放的数量(通过预处理实现)。

      假设get_ans函数可以快速查询区间的最大放的数量,那么如果我想放入一个[l0,r0]的区间,那么需要满足get_ans(l,r)=get_ans(l,l0-1)+get_ans(r0+1,r)+1,这个式子很容易想通。l是离l0最近的上次插入的区间的右端点,r是离r0最近的上次插入的左端点。开始我还在想怎么维护选择区间之后,这个区间的数量变化情况,事实上我们并不关心怎么变,我们只知道它不会使得数量变化即可。也就是说,我只要知道我能影响的区间怎么变,外面的区间因为没有修改,所以肯定也不会变化,换而言之当前影响区间不变则整个区间的总数量就不会变。

       那么我们需要预处理什么呢?首先我们需要在log n的时间内实现快速查询给定范围最大可以选取的区间数量。那么我们把区间重新排序,并且去掉互相包含的区间中较大的部分(显然可以知道这是可行的),这可以先按左端点排序,再用一个右端点单调递增的栈实现。之后对于每个保留下来的区间i,考虑用f[i][j]表示从第i个区间开始往后连续选取2^j个区间之后落在哪个区间上。这个预处理也很简单,n log n。这其实用的就是一个倍增的思想。所以对于每一次查询[l,r],我可以找到[l,r]中的第一个区间,然后倍增的找区间,不断地往后跳并累加答案。通过上述操作可以实现查询操作。

       另外,我们选取了一个区间之后可以用一个set维护一下我选取的区间的端点坐标,方便我快速查询最近的坐标。

       整个题目细节比较多,实现起来稍显复杂,需要一点时间。

     

     1 //It is made by ljh2000
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #include <algorithm>
     8 #include <ctime>
     9 #include <vector>
    10 #include <queue>
    11 #include <map>
    12 #include <set>
    13 using namespace std;
    14 typedef long long LL;
    15 const int inf = (1<<30);
    16 const int MAXN = 400011;
    17 int n,cnt,end,ans;
    18 int L[MAXN],nn;
    19 bool pd[MAXN];
    20 int f[MAXN][19];//f[i][j]表示第i个区间往后取2^j个可行的区间后的可以取到第几个区间
    21 struct node{
    22     int l,r;
    23 }a[MAXN],b[MAXN];
    24 set<int>S;
    25 inline bool cmpl(node q,node qq){ if(q.l==qq.l) return q.r>qq.r; return q.l<qq.l; }
    26 inline int getint()
    27 {
    28     int w=0,q=0; char c=getchar();
    29     while((c<'0' || c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); 
    30     while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); return q ? -w : w;
    31 }
    32 
    33 inline void build(){
    34     for(int i=1;i<=n;i++) b[i]=a[i]; sort(b+1,b+n+1,cmpl);
    35     cnt=0; b[++cnt]=b[1];
    36     for(int i=2;i<=n;i++) {
    37     while(b[i].r<=b[cnt].r && cnt>0) cnt--;       
    38     b[++cnt]=b[i];
    39     }
    40     nn=0; for(int i=1;i<=cnt;i++) L[++nn]=b[i].l;
    41     L[++nn]=inf; int xia; for(int i=0;i<=18;i++) f[cnt+1][i]=cnt+1;
    42     for(int i=1;i<=cnt;i++) { xia=upper_bound(L+1,L+nn+1,b[i].r)-L; f[i][0]=xia; }
    43     for(int j=1;j<=18;j++) for(int i=1;i<=cnt;i++) if(f[i][j-1] && f[f[i][j-1]][j-1]) f[i][j]=f[f[i][j-1]][j-1];
    44 }
    45 
    46 inline int get_ans(int l,int r){//得到区间[l,r]的可选区间的个数
    47     if(l>r) return 0; if(l>end) return 0;//!!!
    48     int daan=1,head,cc; head=lower_bound(L+1,L+nn+1,l)-L;
    49     if(head>cnt) return 0;//!!!
    50     if(b[head].r>r) return 0;
    51     for(int i=18;i>=0;i--) {
    52     if(f[head][i]==cnt+1) continue; if(f[head][i]==0) continue;
    53     cc=b[f[head][i]].r; if(cc>r) continue;
    54     head=f[head][i]; daan+=(1<<i);    
    55     }    
    56     return daan;
    57 }
    58 
    59 inline void work(){
    60     n=getint(); for(int i=1;i<=n;i++) a[i].l=getint(),a[i].r=getint(),L[++cnt]=a[i].l,L[++cnt]=a[i].r;
    61     sort(L+1,L+cnt+1); end=nn=unique(L+1,L+cnt+1)-L-1; 
    62     for(int i=1;i<=n;i++) a[i].l=lower_bound(L+1,L+nn+1,a[i].l)-L,a[i].r=lower_bound(L+1,L+nn+1,a[i].r)-L;
    63     build(); int now,nowl,nowr; ans=get_ans(1,end); printf("%d
    ",ans);
    64     S.insert(-inf); S.insert(inf);  bool flag=false;
    65     for(int i=1;i<=n;i++) {
    66     now=*S.lower_bound(a[i].l);
    67     if(now==inf || !pd[now]) {
    68         if(now<=a[i].r) continue;
    69         nowl=*--S.lower_bound(a[i].l); nowl++;
    70         nowr=*S.lower_bound(a[i].r); nowr--;
    71         now=get_ans(nowl,a[i].l-1)+get_ans(a[i].r+1,nowr)+1; 
    72         if(now<get_ans(nowl,nowr)) continue;
    73         S.insert(a[i].l); S.insert(a[i].r); if(a[i].l!=a[i].r) pd[a[i].r]=1;//!!!
    74         if(flag) printf(" ");  printf("%d",i); flag=true;
    75     }    
    76     }
    77 }
    78 
    79 int main()
    80 {
    81     work();
    82     return 0;
    83 }
  • 相关阅读:
    SqlCeConnectionBeginTransaction 方法
    父子继承窗体,子窗体视图无法正常打开,解决办法
    Windows Mobile 如何和模拟器关联有用的URL
    Windows Mobile 6.5.3 Developer Tool Kit
    通过Eclipse import导入项目,並重新命名Project
    【杂】Oracle使用记录:分区表及执行计划
    实践 2-0 selenium使用的一些总结
    实践2-1 python连接Oracle数据库
    【杂】word文件加密和压缩加密
    【杂】HIVE使用记录:回收站及从回收站恢复分区表
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/5951039.html
Copyright © 2020-2023  润新知