ARC128C 简略题解
翻译自:Editorial - Daiwa Securities Co. Ltd. Programming Contest 2021(AtCoder Regular Contest 128) 有改动
不考虑 M 的限制:
-
求出数组 A 后缀和 \(sum[i]\)
-
求 m 为 sum[i]/i 最大的一个 i
-
构造前 N-m 个系数为0,后面 m 个系数为 S/m
正确性:
调整法。设存在最优解 x ,使得 $x_i<x_{i+1} (0\leq i\leq N-1) $,除了 \(i=N-m\)。假设 x_0=0。令 d 为 x 的差分数组。对于每一个 i ,我将倒数 i 个数减去 d_i,最后 m 个数加上 \(d_i\times(N-i)/m\)。这样,我们抹平了 x_i 和 \(x_{i+1}\) 的差额的同时满足系数总额为 S。反复进行,可以达到上述情况。
考虑 M 的限制:
- 如果 \(S/m\leq M\),那最优解就是让最后 m 个数系数为 S/m
- 如果 \(S/m> M\),那最优解最后 m 个系数就是 M,问题转化为前 N-m 个数里面分配系数,使得系数单调,上界 M,总和为 \(S-m*M\)
O(n^2) 复杂度
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int a[N];
int n,m,s;
double solve(int l,int r,double s){
double mx=0,suf=0; int pos=-1;
for(int i=r;i>=l;--i){
suf+=a[i];
double tmp=suf/double(r-i+1);
if(mx<tmp)mx=tmp,pos=i;
}
int len=r-pos+1;
double tmp=s/double(len);
if(tmp<=double(m))return tmp*mx*len;
else return double(m)*mx*len+solve(l,r-len,s-m*len);
}
signed main(){
cin>>n>>m>>s;
for(int i=0;i<n;++i)
cin>>a[i];
double ans=solve(0,n-1,s);
cout<<fixed<<setprecision(10)<<ans<<endl;
return 0;
}