【洛谷5308】[COCI2019] Quiz(WQS二分+斜率优化DP)

点此看题面

大致题意: 你要把\(n\)划分恰好\(k\)个非空段,设第\(i\)段长为\(a_i\),求\(\sum_{i=1}^k\frac{a_i}{\sum_{j=1}^ia_j}\)的最大值。

\(WQS\)二分

考虑到恰好\(k\)段这个限制,显然划分为更多的段数必然能够得到更优的答案,因此可以\(WQS\)二分。

具体地,二分选择一段的额外代价\(C\),只要在每次选取新一段时给答案减去\(C\)即可。

斜率优化\(DP\)

考虑我们设\(f_i\)表示划分出\(1\sim 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;//最终答案
}
posted @ 2020-06-16 13:42  TheLostWeak  阅读(18)  评论(0编辑  收藏