Codeforces Round #390 (Div. 2) D - Fedor and coupons (贪心)

本文介绍了一种从多个区间中选取特定数量的区间,并使这些区间的交集尽可能长的算法实现。通过使用优先队列来维护当前考虑的区间集合,确保能够找到满足条件的最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

从n个区间中选k个,满足这k个区间的交集最长,输出这k个区间的序号,任意可行解均可

思路

官方题解是二分结果(长度),然后将区间缩短ret-1后进行判断看是否存在k个区间,他们的交集长度不为0,对于修改后的区间从左到右扫描即可,用来标记区间是否在当前考虑范围内的代码挺有意思:

for (int i = 1; i <= n; i++) {
    if (r[i] - len > l[i]) {
        evs.push_back(event(l[i], 1));
        evs.push_back(event(r[i] - len, -1));
    }
}

下面抄袭的是某个大神的思路:既然我们要求交集,那么我们可以维护这样一个队列,队列首部的元素右端点最小,队列尾部的元素左端点最大,那么就可以保证这个队列中所有区间的交集就是队首元素右端点减去队尾元素(其实也就是新加进去的元素)的左端点
是不是有一种单调队列的感觉,代码可以写的比官方题解还要简洁,反正我是没有想到

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define debug(a) printf("a =: %d\n",a);
const int INF=0x3f3f3f3f;
const int maxn=3e5+50;
const int Mod=1000000007;
const double PI=acos(-1);
typedef long long ll;
typedef unsigned int ui;
using namespace std;

struct Node{
    int l,r,id;
    bool operator<(const Node &rhs) const{
        return r>rhs.r;
    }
};

bool cmp(const Node &x,const Node &y){
    return x.l<y.l;
}

Node s[maxn];
int n,k;

int main()
{
#ifndef ONLINE_JUDGE
      freopen("in.txt","r",stdin);
#endif

    while(~scanf("%d %d",&n,&k)){
        for(int i=1;i<=n;i++){
            scanf("%d %d",&s[i].l,&s[i].r);
            s[i].id=i;
        }
        sort(s+1,s+1+n,cmp);
        priority_queue<Node> qu;
        int ansL,ansR,maxLen=-1;
        for(int i=1;i<=n;i++){
            qu.push(s[i]);
            if(qu.size()>k) qu.pop();
            if(qu.size()==k){
                int r=qu.top().r;
                int l=s[i].l;
                if(r-l>maxLen){
                    maxLen=r-l;
                    ansL=l; ansR=r;
                }
            }
        }
        printf("%d\n",maxLen+1);
        if(maxLen+1==0){
            for(int i=1;i<=k;i++) printf("%d ",i);
        }else{
            vector<int> ans;
            for(int i=1;i<=n;i++){
                if(s[i].l<=ansL && s[i].r>=ansR && ans.size()<k){
                    ans.push_back(s[i].id);
                }
            }
            sort(ans.begin(),ans.end());
            for(int i:ans) printf("%d ",i);
        }
        puts("");

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值