竞赛讨论区 > 【每日一题】12月24日题目精讲
头像
王清楚
编辑于 2020-12-24 10:58
+ 关注

【每日一题】12月24日题目精讲

题号 NC112136
名称 Hacker, pack your bags!
来源 CF822C
戳我进入往期每日一题汇总贴~
往期每日一题二期题单

图片说明

感谢 YangYL° 推荐题目,100牛币奖励已发放~
如果你在题库做题时遇到了喜欢的题目,欢迎推荐给邓老师~ 点击查看详情
每日一题QQ群:659028468

题解

前言

这是一道很棒的思维题/数据结构!

难度:2星
做法:二分,排序 线段树

题意:

给定 以及 个区间,区间 有左端点 和 右端点 以及 花费 ,要求你选出两个区间,满足下面两个条件:

  • 两个区间没有交集
  • 两个区间的长度和等于 (这里的长度为 )

现在要求你选出的这两个区间的权值和最小,输出最小权值和。

做法:

因为是两个区间,考虑枚举其中一个区间。

因为两个区间要不相交,所以很容易想到把所有区间按照其 从小到大排序,如果 相等,则按照 从小到大排序。

通过枚举区间 ,只需要找到所有左端点 大于其右端点 的区间 ,这样子 , 两个区间就是不相交的。

可以通过二分找到第一个左端点 > 的区间,这样子所有左端点在这个找到的区间的左端点右边的区间都是满足条件的区间,假设找到的是第 个区间,因为所有区间是按 左端点 排序好了的,所以从 第 到 第 个区间跟当前区间 都是没有交点的。

找到 内满足 的所有区间的权值最小值,也就是跟当前区间 的长度之和为 的 所有区间中区间的权值最小的区间。
( 直接设置为 , 用之前提到的二分找到,如果不存在 ,那么说明这个区间是无法与其他区间满足条件的)

这样子就满足了第一个限制,并且每一张票都有了一个自己查询的区间:

对于枚举的每一个区间都进行查询很不方便,但是感觉可以用神奇数据结构来搞(目测线段树?

观察到每次要查的右区间是固定的,不妨将枚举到 的时候需要查询的 给记录下来,然后把 按照从大到小排序。

这样子 O() 扫一遍过来,开个桶在扫的时候记录一下 ,也就是长度为 的所有区间的权值最小值,这时候对于询问就直接O(1) 获得答案。

Code:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 400005,INF = 100000000000000000;
int n , m ;
int Min[MAXN],cnt = 0;

struct Node {
    int l,r,cost;
} T[MAXN];

struct P {
    int L,D,F;
    //L记录查[L,R]的左端点L,D表示枚举到的区间的权值,F表示要找的长度 即长度为 m-lena 
} C[MAXN];

bool cmp(Node A , Node B)
{
    if(A.l != B.l)
    return A.l < B.l;
    return A.r < B.r;
}

int cmp1(P A, P B)
{
    return A.L > B.L;
}

inline int read()
{
    int x = 0 , flag = 1;
    char ch = getchar();
    for( ; ch > '9' && ch < '0' ; ch = getchar())if(ch == '-')flag = -1;
    for( ; ch >= '0' && ch <= '9' ; ch = getchar())x = (x << 3) + (x << 1) + ch - '0';
    return x * flag;
}

signed main()
{
    n = read() , m = read();
    for(int i = 1 ; i <= n ; i ++)
    T[i].l = read(),T[i].r = read(),T[i].cost = read();
    sort(T + 1 , T + 1 + n , cmp);//按照左端点从小到大排序,左端点相同的按照右端点从小到达排序
    for(int i = 1 ; i <= m ; i ++)Min[i] = INF;
    for(int i = 1 ; i <= n ; i ++)
    {
        int lena = T[i].r - T[i].l + 1;
        if(lena >= m){C[i].L = -1;continue;}
                //剪枝了一下,如果当前区间长度大于等于m了,另外一个区间需要长度小于等于0,不存在答案
        int K = n + 1;
        for(int j = log2(n - i + 1) ; j >= 0 ; j --)
            if(T[K - (1 << j)].l > T[i].r && K - (1 << j) >= 1)K -= (1 << j);//二分换成倍增了。
        if(K != n + 1)
            C[i].L = K , C[i].D = T[i].cost , C[i].F = m - lena;
        else C[i].L = -1;//题解里说的L不存在,赋值为-1
    }
    sort(C + 1 , C + 1 + n , cmp1);//按照L排序
    int now = 1,Ans = INF;
    for(int i = n ; i >= 1 ; i --)
    {
        if(C[now].L == -1)break;//如果没有询问了直接break
        int len = T[i].r - T[i].l + 1;
        Min[len] = min(Min[len],T[i].cost);//扫的时候记录长度为len的区间的权值最小值
        while(C[now].L == i && now <= n)一个点可能有多个询问,用while
        {
            if(C[now].L == -1)break;//没有询问了
            Ans = min(Ans,C[now].D + Min[C[now].F]);//O(1)获得答案,取min
            now ++;
        }
    }
    if(Ans == INF)    cout << -1;
    else cout << Ans;
    return 0;
}

下面是无注释版本的代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 400005,INF = 100000000000000000;
int n , m ;
int Min[MAXN],cnt = 0;

struct Node {
    int l,r,cost;
} T[MAXN];

struct P {
    int L,D,F;
} C[MAXN];

bool cmp(Node A , Node B)
{
    if(A.l != B.l)
    return A.l < B.l;
    return A.r < B.r;
}

int cmp1(P A, P B)
{
    return A.L > B.L;
}

inline int read()
{
    int x = 0 , flag = 1;
    char ch = getchar();
    for( ; ch > '9' && ch < '0' ; ch = getchar())if(ch == '-')flag = -1;
    for( ; ch >= '0' && ch <= '9' ; ch = getchar())x = (x << 3) + (x << 1) + ch - '0';
    return x * flag;
}

signed main()
{
    n = read() , m = read();
    for(int i = 1 ; i <= n ; i ++)
    T[i].l = read(),T[i].r = read(),T[i].cost = read();
    sort(T + 1 , T + 1 + n , cmp);
    for(int i = 1 ; i <= m ; i ++)Min[i] = INF;
    for(int i = 1 ; i <= n ; i ++)
    {
        int lena = T[i].r - T[i].l + 1;
        if(lena >= m){C[i].L = -1;continue;}
        int K = n + 1;
        for(int j = log2(n - i + 1) ; j >= 0 ; j --)
            if(T[K - (1 << j)].l > T[i].r && K - (1 << j) >= 1)K -= (1 << j);
        if(K != n + 1)
            C[i].L = K , C[i].D = T[i].cost , C[i].F = m - lena;
        else C[i].L = -1;
    }
    sort(C + 1 , C + 1 + n , cmp1);
    int now = 1,Ans = INF;
    for(int i = n ; i >= 1 ; i --)
    {
        if(C[now].L == -1)break;
        int len = T[i].r - T[i].l + 1;
        Min[len] = min(Min[len],T[i].cost);
        while(C[now].L == i && now <= n)
        {
            if(C[now].L == -1)break;
            Ans = min(Ans,C[now].D + Min[C[now].F]);
            now ++;
        }
    }
    if(Ans == INF)    cout << -1;
    else cout << Ans;
    return 0;
}

另外建议不要用,如果用评测得很慢,虽然不会超时,但是你得评测分半钟.....(因为一共136个点!)
欢迎各位大佬来做题写题解,也欢迎大家踊跃在当日讨论贴中提问!

活动奖励:

在牛客博客中写出题解,并回复地址
审核通过可获得(依据题目难度和题解的内容而定)

本道题目12月31日中午12:00之前写的题解有获得牛币资格~

.牛币兑换中心

牛客博客开通方式

  1. 如何开通牛客博客:https://www.nowcoder.com/discuss/202952
  2. 如何使用博客搬家功能:进入博客--->设置--->底部博客搬家
  3. 如果你对牛客博客有任何意见或建议:牛客博客意见反馈专贴

全部评论

(4) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐