1178: [Apio2009]CONVENTION会议中心

1178: [Apio2009]CONVENTION会议中心

Time Limit: 15 Sec   Memory Limit: 162 MB
Submit: 824   Solved: 327
[ 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

[ Submit][ Status][ Discuss]




去掉第二个询问就是经典贪心,,,

现在问题是第二个要怎么处理???


回归第一问

我们去掉所有包含关系的区间中较大的那个,对结果一定不会有影响

因为如果某个最优方案有那个大的区间,那选小的一定没有差别

这样我们就得到一些小区间,这些区间的左右端点都是严格递增的

当我们从这些区间里选择最多的,两两不相交的区间,那就是第一问的答案了

贪心即可


考虑第二问,

对于一个区间,如果选择它,对最优答案没有影响,那显然这个区间是可以选的

这样按时间顺序考虑每个区间,每次检查这个区间选了会不会让答案变差,如果不会,就要它

如何检查??

显然,当前考虑的区间中,若有一个位置已经被覆盖过,那这个区间一定不能选

若全部为空,那这个区间一定包含于一个较大的空区间,记这个空区间为[i,j],记当前区间[l,r]

那么如果我们知道[i,j]、[i,l)以及(r,j]的最优答案,就能判断了

而之前已经处理出一些两两不互相包含的区间

将它们按照左端点升序排序

记Right[i]为第i个区间向右,第一个与它不相交的区间

对于[l,r],我们找出所有包含于[l,r]的区间,不断地沿Right数组走,就能找出答案

这走法可以用倍增算法优化成O(logn)


#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 4E5 + 10;
const int INF = ~0U>>1;
const int T = 16;
 
int n,cnt,top,tot,L[maxn],R[maxn],L2[maxn],R2[maxn],c[maxn*T]
    ,cur = 1,ans,Right[maxn][20],s[maxn],Num[maxn*2];
bool Mark[maxn*T];
 
void Read()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d",&L[i],&R[i]);
        Num[++tot] = L[i];
        Num[++tot] = R[i];
    }
    sort(Num + 1,Num + tot + 1);
    for (int i = 2; i <= tot; i++)
        if (Num[i] != Num[i-1])
            Num[++cur] = Num[i];
    Num[++cur] = INF;
    L[0] = R[0] = cur;
    for (int i = 1; i <= n; i++) {
        L[i] = lower_bound(Num + 1,Num + cur + 1,L[i]) - Num;
        R[i] = lower_bound(Num + 1,Num + cur + 1,R[i]) - Num;
        if (R[R2[L[i]]] > R[i])
            R2[L[i]] = i;
    }
}
 
void Work()
{
    for (int i = 1; i <= cur; i++) {
        if (!R2[i]) continue;
        while (top && R[s[top]] >= R[R2[i]]) --top;
        s[++top] = R2[i];
    }
    for (int i = 1; i <= top; i++)
        L2[i] = L[s[i]],R2[i] = R[s[i]];
    int Now = top + 1;
    L2[Now] = R2[Now] = cur;
    for (int i = top; i; i--) {
        while (L2[Now - 1] > R2[i]) --Now;
        Right[i][0] = Now;
        if (Right[i][0] > top) Right[i][0] = 0;
    }
    for (int j = 1; j < 20; j++)
        for (int i = 1; i <= top; i++)
            Right[i][j] = Right[Right[i][j-1]][j-1];
}
 
int Query(int l,int r)
{
    if (l > r) return 0;
    int pl = lower_bound(L2 + 1,L2 + top + 1,l) - L2;
    int pr = lower_bound(R2 + 1,R2 + top + 1,r) - R2;
    if (R2[pr] > r || pr > top) --pr;
    if (pl > pr) return 0;
    int pos = pl,ret = 1;
    for (int j = 19; j >= 0; j--) {
        if (!Right[pos][j]) continue;
        if (Right[pos][j] <= pr) {
            ret += (1<<j);
            pos = Right[pos][j];
        }
    }
    return ret;
}
 
void pushdown(int o,int l,int r)
{
    if (Mark[o]) {
        Mark[o<<1] = Mark[o<<1|1] = 1;
        int mid = (l + r) >> 1;
        c[o<<1] = mid - l + 1;
        c[o<<1|1] = r - mid;
    }
}
 
void Modify(int o,int l,int r,int ml,int mr)
{
    if (ml <= l && r <= mr) {
        Mark[o] = 1;
        c[o] = r - l + 1;
        return;
    }
    pushdown(o,l,r);
    int mid = (l + r) >> 1;
    if (ml <= mid) Modify(o<<1,l,mid,ml,mr);
    if (mr > mid) Modify(o<<1|1,mid+1,r,ml,mr);
    if (Mark[o<<1] && Mark[o<<1|1]) Mark[o] = 1;
    c[o] = c[o<<1] + c[o<<1|1];
}
 
int Lower(int o,int l,int r,int ql,int qr)
{
    int mid = (l + r) >> 1;
    if (ql <= l && r <= qr) {
        if (!c[o]) return cur;
        if (Mark[o]) return l;
        pushdown(o,l,r);
        if (c[o<<1]) return Lower(o<<1,l,mid,ql,qr);
        else return Lower(o<<1|1,mid + 1,r,ql,qr);
    }
    pushdown(o,l,r);
    int ret = cur;
    if (ql <= mid) ret = min(ret,Lower(o<<1,l,mid,ql,qr));
    if (ret < cur) return ret;
    if (qr > mid) ret = min(ret,Lower(o<<1|1,mid+1,r,ql,qr));
    return ret;
}
 
int Upper(int o,int l,int r,int ql,int qr)
{
    int mid = (l + r) >> 1;
    if (ql <= l && r <= qr) {
        if (!c[o]) return 0;
        if (Mark[o]) return r;
        pushdown(o,l,r);
        if (c[o<<1|1]) return Upper(o<<1|1,mid+1,r,ql,qr);
        else return Upper(o<<1,l,mid,ql,qr);
    }
    pushdown(o,l,r);
    int ret = 0;
    if (qr > mid) ret = max(ret,Upper(o<<1|1,mid+1,r,ql,qr));
    if (ret) return ret;
    if (ql <= mid) ret = max(ret,Upper(o<<1,l,mid,ql,qr));
    return ret;
}
 
int Sum(int o,int l,int r,int ql,int qr)
{
    if (ql <= l && r <= qr) return c[o];
    pushdown(o,l,r);
    int mid = (l + r) >> 1,ret = 0;
    if (ql <= mid) ret += Sum(o<<1,l,mid,ql,qr);
    if (qr > mid) ret += Sum(o<<1|1,mid+1,r,ql,qr);
    return ret;
}
 
int main()
{
    //freopen("1178.in","r",stdin);
    //freopen("1178.out","w",stdout);
     
    Read();
    Work();
    Modify(1,0,cur,0,0);
    Modify(1,0,cur,cur,cur);
    ans = Query(1,cur);
    printf("%d\n",ans);
    int K = 0;
    for (int i = 1; i <= n; i++) {
        if (Sum(1,0,cur,L[i],R[i]) > 0) continue;
        int l = Upper(1,0,cur,0,L[i] - 1);
        int r = Lower(1,0,cur,R[i] + 1,cur);
        ++l; --r;
        int Need = Query(l,r);
        int suml = Query(l,L[i] - 1);
        int sumr = Query(R[i] + 1,r);
        if (suml + sumr + 1 == Need) {
            ++K;
            if (K < ans) printf("%d ",i);
            else printf("%d",i);
            Modify(1,0,cur,L[i],R[i]);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值