题解:AT_arc215_a [ARC215A] Zombie
题意已经够简洁了不再赘述。
思路:如果我们把诱饵放在两僵尸之间,相当于合并了两只僵尸,答案加上这两僵尸距离的一半,并且两端的僵尸距道路两端距离加上同样的值。转换一下,我们有三种操作:
- 选择左端的贡献,在右端加上左端贡献并清空;
- 选择右端的贡献,在左端加上右端贡献并清空;
- 选择中间的贡献,在左右两端加上中间贡献并清空。
假设我们的 \(k\) 足够大,那么最后答案显然是选择若干中间的贡献,然后选择左右两端较大值,最后一直在左端右端来回。
形式化的,令中间的贡献为 \(w_i\),即两只僵尸之间距离的一半。左端贡献为 \(x\),即最左端僵尸距道路左端的距离。右端贡献为 \(y\),即最右端僵尸距道路右端的距离。答案就是 \(\sum w_i +\max(x,y)+(k-1-i)\times (x+y+\sum w_i)\)。显然我们要优先选择 \(w_i\) 大的。
我们先算出 \(w_i\),求出前缀和,枚举 \(i\) 计算答案并取最大值就是最后的答案。
但是如果 \(k\) 不够大,可能全选择中间的贡献才是最优的。所以我们先取中间的贡献,直到取中间的贡献不优于取两端的贡献。
时间复杂度 \(\mathcal{O}(n)\)。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define endl '\n'
const int N=2e5+5;
#define ll long long
int n,k,L,a[N];
ll val[N];
inline void solve(){
cin>>n>>k>>L;
for(int i=1;i<=n;++i)cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<n;++i)val[i]=(a[i+1]-a[i])/2;
sort(val+1,val+n);
ll x=a[1],y=L-a[n],res=0;
--n;
while(n&&k){
if(val[n]>=max(x,y)){
res+=val[n];
x+=val[n];
y+=val[n];
--k;--n;
}
else break;
}
if(!k)return void(cout<<res<<endl);
if(k==1)return void(cout<<res+max(x,y)<<endl);
res+=1ll*max(x,y)+(k-1ll)*(1ll*(x+y));
sort(val+1,val+n+1,[](int vx,int vy){
return vx>vy;
});
for(int i=1;i<=n;++i)val[i]+=val[i-1];
ll mx=0;
for(int i=1;i<=min(k,n);++i)
mx=max(mx,1ll*val[i]*(2ll*(k-i))-1ll*i*(1ll*(x+y)));
cout<<res+mx<<endl;
return;
}
int main(){
IOS;
int _=1;cin>>_;
while(_--)solve();
return 0;
}
/*
华风夏韵,洛水天依!
*/
记录。

浙公网安备 33010602011771号