大致题意: 你要把(n)划分恰好(k)个非空段,设第(i)段长为(a_i),求(sum_{i=1}^kfrac{a_i}{sum_{j=1}^ia_j})的最大值。
(WQS)二分
考虑到恰好(k)段这个限制,显然划分为更多的段数必然能够得到更优的答案,因此可以(WQS)二分。
具体地,二分选择一段的额外代价(C),只要在每次选取新一段时给答案减去(C)即可。
斜率优化(DP)
考虑我们设(f_i)表示划分出(1sim i)所能得到的最大收益,暴力想法就是枚举一个(j),得到:
[f_i=f_j+frac {i-j}i-C=f_j-frac ji+1-C
]
其中(1-C)明显是常数项,因此对于两个转移点(x,y)((x>y)),(x)的答案优于(y)的答案需要满足:
[f_x-frac xi>f_y-frac yi
]
[frac{x-y}i<f_x-f_y
]
[i>frac {x-y}{f_x-f_y}
]
因此我们只要开一个单调队列,维护斜率单调递增,每次弹出不再有贡献的队首,再从队首转移即可。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define DB long double
#define eps 1e-12
using namespace std;
int n,k,q[N+5],g[N+5];DB f[N+5];//f记录最大收益,g记录划分段数
I int Check(Con DB& C)//斜率优化DP,C为当前额外代价
{
RI i,H=1,T=0;for(q[++T]=0,i=1;i<=n;++i)//初始放入0
{
W(H<T&&i*(f[q[H+1]]-f[q[H]])>q[H+1]-q[H]) ++H;f[i]=f[q[H]]-1.0*q[H]/i+1-C,g[i]=g[q[H]]+1;//弹出不优队首,然后从队首转移
W(H<T&&(q[T]-q[T-1])*(f[i]-f[q[T]])>(i-q[T])*(f[q[T]]-f[q[T-1]])) --T;q[++T]=i;//在队尾加入当前点,维护斜率递增
}return g[n];//返回在当前额外代价下的最大收益
}
int main()
{
scanf("%d%d",&n,&k);DB l=0,r=1e9,mid;
W(r-l>eps) Check(mid=(l+r)/2)<=k?r=mid:l=mid;//WQS二分
return Check(r),printf("%.9Lf",f[n]+k*r),0;//最终答案
}