Codeforces Round #310 (Div. 1) B. Case of Fugitive

题目链接

给定N段互不相交且有序的区间,用M座桥把他们连接成一段区间。如果存在相邻的两个区间分别有两个点X,Y满足|Y-X| = Li,Li是第i座桥的长度,那么就可以用第i座桥把这两个区间连接起来。问这M座桥是否可以将他们连接成一段区间,如果可以,输出任意一组合法的方案。

思路

考虑相邻的两个区间[Li,Ri],[Li+1,Ri+1],可以连接他俩的桥的范围为[Li+1-Ri,Ri+1-Li],现在题目变成了给定N段区间和M个点,问每个区间是否可以不重复的覆盖一个点,这个问题可以用贪心解决:将所有区间按照右端排序,依次给每个区间覆盖最靠左的一个未被覆盖过的点,如果一个区间无法找到可以覆盖的点,那么输出无解。

贪心的证明如下:
对于区间i,存在两个可以覆盖的点X,Y,且有X < Y,如果区间i覆盖点Y合法,那么区间i覆盖X也合法,贪心证毕。对于任意区间K,K的右端大于 i的右端,若之前K可以覆盖Y之前的点合法,那么现在K也可以覆盖Y合法;若之前K可以覆盖Y之后的点合法,那么现在依然可以覆盖。调整成功,证毕。

比赛的时候想到了这个方法,然而还是太菜了T^T。赛后用multiset重载 < 号过了这道题,STL用得太差。。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define bit_cnt(x) __builtin_popcount((unsigned)x)
#define foru(i, a, b) for(int i=(a); i<=(b); i++)
#define ford(i, a, b) for(int i=(a); i>=(b); i--)
#define clr(a, b) memset(a, (b), sizeof(a))
typedef long long ll;
const double Pi = 4 * atan(1.0);

inline int readint() {
    char c = getchar();
    while(!isdigit(c)) c = getchar();
    int ret = 0;
    while(isdigit(c)) ret = ret * 10 + c - '0', c = getchar();
    return ret;
}

/**************************************************
Header
***************************************************/

const int N = 200010;

struct line{
    ll st, en;
    int idx;
}L[N];

struct point{
    ll x;
    int idx;
    bool operator <(const point B)const{
        if (x < B.x) return 1;
        return 0;
    }
}tmp;

int n, m, g[N];
ll preL, preR, nowL, nowR;

bool cmp(line A, line B){
    if (A.en < B.en) return 1;
    return 0;
}

multiset<point> Set;
multiset<point>::iterator it;
bool solve(){
    foru(i, 1, n){
        if (! Set.size()) return 0;
        tmp.x = L[i].st; tmp.idx = -1;
        it = Set.lower_bound(tmp);
        if (it == Set.end()) return 0;
        if ((*it).x > L[i].en) return 0;
        g[L[i].idx] = (*it).idx;
        Set.erase(*it);
    }
    return 1;
}

int main(){
    //freopen("B.txt", "r", stdin);
    while (scanf("%d %d", &n, &m) != EOF){
        n --;
        scanf("%I64d %I64d", &preL, &preR);
        foru(i, 1, n){
            scanf("%I64d %I64d", &nowL, &nowR);
            L[i].st = nowL - preR;
            L[i].en = nowR - preL;
            L[i].idx = i;
            preL = nowL; preR = nowR;
        }
        sort(L + 1, L + 1 + n, cmp);

        Set.clear();
        foru(i, 1, m){
            scanf("%I64d", &tmp.x);
            tmp.idx = i;
            Set.insert(tmp);
        }

        if (! solve()) puts("No");
        else {
            puts("Yes");
            foru(i, 1, n-1) printf("%d ", g[i]);
            printf("%d\n", g[n]);
        }
    }
    return 0;
}

重载set

struct point{ 
    ll x;
    int idx;
    bool operator <(const point B)const{
        if (x < B.x) return 1; //  这是multiset的写法
        return 0;
    }
};

删除的时候
multiset<point>::iterator it,
multiset.erase(it)
multiset单点删除要删迭代器

struct point{
    ll x;
    int idx;
    bool operator <(const point B)const{
        if (x < B.x) return 1;//这是set的写法
        if (x == B.x && idx < B.idx)
        return 0;
    }
};

需要注意的是由于set不能有重复的元素,然而struct中有两维
如果没有重载第二维的比较,那么第一维相同的点只会留下一个

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值