"蔚来杯"2022牛客暑期多校训练营2 H
题意: 有 n n n 个人要坐电梯,从 a a a 层坐到 b b b 层,电梯满载为 m m m 人,总楼层为 k k k 层,电梯上升可以一层一层的上升,任意层都可以选择下降。但是下降必须降到 1 1 1 层为止才可以重新上升。电梯在层与层之间的移动消耗的能量为 1 1 1 ,问我们最少花费多少代价使得所有人成功到达目的地。
思路: 有前缀和算法知识前提,我们首先看上升人员,我们统计出每一层至少被电梯经过多少次,可以使用差分,最后的时候合并。例如如果从 3 3 3 层楼到 6 6 6 层楼,我们通过 O ( 1 ) O(1) O(1) 在 4 4 4 层楼位置 + 1 +1 +1, 7 7 7 号楼位置 − 1 -1 −1 即可。代表 4 − 6 4-6 4−6 号楼经过次数 + 1 +1 +1, 3 3 3 层楼不必增加因为 3 3 3 层楼不需要消耗电梯能量。那么最后前缀和操作统计出所有楼层至少人员经过次数,并将每一层除以 m m m 上取整,表示 在满载情况下 ,电梯 仍需 经过当前楼层多少次。并且有一点显然,如果上层楼至少经过数大于下方楼层,那么下方楼层同样需要电梯经过与上方楼层同样次数。例如 7 7 7 层至少经过 3 3 3 次,那么 7 7 7 层及以下楼层电梯经过次数不可能低于 3 3 3 次。最后可以获得上升楼层电梯花费能量的最小值。接下去讨论下降楼层,会发现下降楼层会有很大一部分和上升楼层重复,每一次上升楼层选择下降然后重新上升的过程同样可以使得很多下降人员到达目标楼层,所以我们需要在同一楼层的 上升和下降 次数中选择一个最大值即可。
代码:
/*
* @author: Snow
* @Description: Algorithm Contest
* @LastEditTime: 2022-07-28 19:40:33
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#pragma GCC optimize(3)
#define re register int
typedef pair<int,int>PII;
#define pb push_back
#define debug(a) cout<<a<<' ';
#define fer(i,a,b) for(re i=a;i<=b;i++)
#define der(i,a,b) for(re i=a;i>=b;i--)
int n,m,k;
const int N = 2e5+10;
int suf[N];
int a[N];
int b[N];
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n>>m>>k;
vector<int>v;
for(int i=1;i<=n;i++){
cin>>a[i];
cin>>b[i];
v.pb(a[i]);
v.pb(b[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int sum=v.size();
vector<int>up,down;
for(int i=1;i<=n;i++){
int l=lower_bound(v.begin(),v.end(),a[i])-v.begin();
int r=lower_bound(v.begin(),v.end(),b[i])-v.begin();
if(l<r){
up[l+1]++;
up[r+1]--;
}
else{
down[r+1]++;
down[l+1]--;
}
}
for(int i=1;i<=sum;i++){
up[i]+=up[i-1];
down[i]+=down[i-1];
int ti1=(up[i]+m-1)/m;
int ti2=(down[i]+m-1)/m;
suf[max(ti1,ti2)]=max(suf[max(ti1,ti2)],v[i]-1);
}
for(int i=n-1;i>=1;i--)suf[i]=max(suf[i],suf[i+1]);
int ans=0;
for(int i=n;i>=1;i--)ans+=suf[i];
cout<<ans<<endl;
return 0;
}