• 【2022牛客多校-2】H - Take the Elevator


    题意

    n 个人坐电梯,楼高 m ,每个人有起始楼层和目标楼层。
    电梯有载客量限制 k ,上升时可以上升到任意层并随时下降,但是下降要一直下降到一层才能再上升。
    电梯每秒运行一层,换方向和上下人不占用时间,问电梯最短运行时间。
    n , m ≤ 2 × 1 0 5 , k ≤ 1 0 9 n, m ≤ 2 × 10^5, k ≤ 10^9 n,m2×105,k109

    思路

    人乘电梯上升或下降是同类问题,在此我们只讨论上升。

    先考虑一个类似的问题:其他条件相同,试求至少提供多少台电梯才能使得每台电梯只上升一次,就运完所有的乘客。
    易发现人从 x 层上升至 y 层使用了电梯的 x → ( x + 1 ) → ( x + 2 ) → ⋯ → ( y − 1 ) → y x\to (x+1)\to(x+2)\to\dots\to(y-1)\to y x(x+1)(x+2)(y1)y

    我们用 ( x + 1 ) (x+1) (x+1) 表示 x → ( x + 1 ) x\to(x+1) x(x+1) 段,再进行抽象,就变成:有一长度为m的全 0 数组,有n次操作,每次操作使得从 l+1 到 r 的区间中所有元素增加1,求所有操作结束后整个数组的最大值除以 k 上取整。

    这个问题非常眼熟,对于每个操作,我们只需要让 a l + 1 = a l + 1 + 1 , a r + 1 = a r + 1 − 1 a_{l+1}=a_{l+1}+1,a_{r+1}=a_{r+1}-1 al+1=al+1+1,ar+1=ar+11。所有操作执行结束后,对 a 数组进行前缀和就可以得到操作后的数列了。

    注意这里我们使用了 ( x + 1 ) (x+1) (x+1) 表示 x → ( x + 1 ) x\to(x+1) x(x+1) 段,导致差分操作整体右移了,为什么呢?
    回到原问题,我们要求的并不是“至少提供多少台电梯才能使得每台电梯只上升一次,就运完所有的乘客”,而是电梯的最小运行时间。该问题与我们已经解决的问题唯一区别是我们需要考虑电梯运行的层数,我们希望电梯每次(每部电梯)运行的最高层数尽可能小。
    在这里插入图片描述
    如图所示,电梯同一时间最多只能载两个人,则①和②乘坐一部电梯、③和④乘坐一部电梯明显更优。
    在这里插入图片描述
    所以自上而下进行遍历,若在高度 i 乘电梯的人数超过负载,只需增设最大高度为 i 的电梯。

    我们只需将操作改为 a l = a l − 1 , a r = a r + 1 a_{l}=a_{l}-1,a_{r}=a_{r}+1 al=al1,ar=ar+1,再自上而下进行前缀和,同时维护当前开设的电梯的数量。若第 ( i − 1 ) → i (i-1)\to i (i1)i 段人数过多,易通过人数、当前电梯数和电梯的最大负载求出需要新增的电梯数 t,则 a n s = a n s + t ∗ ( i − 1 ) ans = ans + t *(i-1) ans=ans+t(i1)

    人乘坐电梯下降思路相同,二者取 max 即可。

    注意该题 k 范围较大,需要进行离散化,同时统计答案时需要进行相应修改。

    代码

    #include 
    #include 
    using namespace std;
    int n,m,k;
    const int N=200005;
    int a[N*2],b[N*2];
    struct asdf{
    	int x,t;
    }inpt[N*2];
    int cmpx(asdf a,asdf b){return a.x<b.x;}
    int cmpt(asdf a,asdf b){return a.t<b.t;}
    long long v[N*2],ans,cnt,t;
    int main()
    {
        scanf("%d%d%d",&n,&k,&m);
        for (int x,y,i=1;i<=n;++i) 
        {
            scanf("%d%d",&x,&y);
            inpt[i*2-1].x=x;
            inpt[i*2-1].t=i*2-1;
            inpt[i*2].x=y;
            inpt[i*2].t=i*2;
        }
        sort(inpt+1,inpt+1+n*2,cmpx);
        int tot=1,o=1;
        v[1]=1;
        for (int i=1;i<=n*2;++i)
        	if (inpt[i].x==o) inpt[i].x=tot;
        	else
    		{
    			o=inpt[i].x;
    			inpt[i].x=++tot;
    			v[tot]=o;
    		}
        sort(inpt+1,inpt+1+n*2,cmpt);
        for (int x,y,i=1;i<=n;++i) 
        {
        	x=inpt[i*2-1].x;
        	y=inpt[i*2].x;
            if (x<y) a[y]++,a[x]--;
            else b[x]++,b[y]--;
        }
        for (int i=tot;i>=1;--i)
        {
            a[i]+=a[i+1];
            b[i]+=b[i+1];
            t=0;
            if (a[i]>cnt*k) t=(a[i]-cnt*k+k-1)/k;
            if (b[i]>cnt*k) t=max(t,(b[i]-cnt*k+k-1)/k);
            ans+=t*(v[i]-1);
            cnt+=t;
        }
        printf("%lld\n",ans*2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    Wnmp服务安装并结合内网穿透实现公网远程访问——“cpolar内网穿透”
    零数科技深耕苏州,受邀参加中国金融科技产业峰会
    Linux应用编程概念
    一文读懂官方给出torch.nn.RNN API的参数及手写RNN API复现
    六级作文---3.图画类
    easyscholar配置秘钥连接Zotero-style,更方便的了解文献!
    单元测试编写规范
    服务器硬件基础知识
    Linux | Centos下几种CPU查看使用率的常用命令
    港联证券:三季报亮相 盈利拐点来了?
  • 原文地址:https://blog.csdn.net/Ice_teapoy/article/details/126004510