2022牛客#2 H,思维,大范围的线段覆盖的差分实现

题意:

有一个大厦,里面有一个电梯和 n n n个需求使用电梯的人,每个人有一个出发楼层 a i a_{i} ai和到达楼层 b i b_{i} bi,这个大厦高 m m m楼,最低是 1 1 1楼,电梯一次最多可以搭载 k k k个人。电梯有一个特性,上行的时候可以在任意地方更改方向向下行,而下行的时候只能在到达 1 1 1楼后再改变方向向上行,当然无论何时都可以停下来上下人,问电梯至少需要几分钟才可以把 n n n个人送达目的楼层并且回到一楼?

方法:

一个人是从 a i a_{i} ai b i b_{i} bi的,那么我们可以在数轴上 a i a_{i} ai b i b_{i} bi的地方覆盖上一条线段,某个地方 x x x覆盖了 t t t条线段,就说明一共有 t t t个人途径这里,那么电梯就一定要到 x x x这个地方 c e i l ( t , k ) ceil(t,k) ceil(t,k)次,那么我们只需要找到最大的这个 c e i l ( t , k ) ceil(t,k) ceil(t,k)。首先我们就得实现线段覆盖,容易想到的方法是线段树区间修改,但难以面对大范围的数据,因此有一种差分线段覆盖的方法,如果要覆盖线段在 [ x , y ] [x,y] [x,y],那么只需要在 x x x这个地方+1, y y y这个地方-1即可。即设定一对 p a i r pair pair f i r s t first first是位置, s e c o n d second second是差分量,那么覆盖 [ x , y ] [x,y] [x,y]就仅需要加入两个 p a i r pair pair < x , 1 > , < y , − 1 > <x,1>,<y,-1> <x,1><y,1>。按照端点位置排序后,当我们读入一个就添加到 s u m sum sum变量内,读入一个之后的 s u m sum sum即使现在这个 p a i r pair pair和下个 p a i r pair pair之间的线段覆盖数。

完成线段覆盖之后,我们就开始找距离和,如果只需要走一次的线段最高覆盖到 h 1 h_{1} h1,走两次的线段最高覆盖到 h 2 h_{2} h2,如果 h 1 < h 2 h_{1}<h_{2} h1<h2,那么我们最高两次都需要到达最高处,如果 h 1 > h 2 h_{1}>h_{2} h1>h2,那么我们的路线是1-> h 2 h_{2} h2->1,1-> h 1 h_{1} h1->1,因此,我们把所有极值点插入 s e t set set,然后从后往前遍历,每次遇到比之前遇到的次数多的极值点,我们就停下来计算这一个极值点与遇到的最大点的次数差需要走多远,加入答案。

#include<bits/stdc++.h>
#define ll long long
#define fr first
#define se second
#define int long long
using namespace std;

int read()
{
    int ret=0,base=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-') base=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        ret=(ret<<3)+(ret<<1)+ch-48;
        ch=getchar();
    }
    return ret*base;
}

inline int ceil_(int a,int b)
{
    return a%b?a/b+1:a/b;
}

vector<pair<int,int>>up,down;
set<pair<int,int>>set1;
int n,k,m;

signed main()
{
    n=read();k=read();m=read();
    for(int i=1;i<=n;i++)
    {
        int u=read(),v=read();
        if(u<v)
        {
            up.push_back(make_pair(u,1));
            up.push_back(make_pair(v,-1));
        }
        else
        {
            down.push_back(make_pair(v,1));
            down.push_back(make_pair(u,-1));
        }
    }
    sort(up.begin(),up.end());
    sort(down.begin(),down.end());
    int sum=0;
    for(int i=0;i<up.size();i++)
    {
        if(up[i].se==-1) set1.insert({up[i].fr,sum});
        sum+=up[i].se;
    }
    sum=0;
    for(int i=0;i<down.size();i++)
    {
        if(down[i].se==-1) set1.insert({down[i].fr,sum});
        sum+=down[i].se;
    }
    auto it=set1.end();it--;
    int max1=0,ans=0;
    while(1)
    {
        int pos=it->fr,t=it->se;
        if(ceil_(t,k)>max1)
        {
            int x=ceil_(t,k)-max1;
            ans+=2*x*(pos-1);
            max1+=x;
        }
        if(it--==set1.begin()) break;
    }
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值