Codeforces - 673E
有 N 个关卡,可以分为
K 块,每个关卡都有个权值 ti
每次选择最早没有通关的关卡块,设这个关卡包含了 [i,j] 的游戏
选到最早没有通关的关卡是 k , 选到k 的概率是 P=tk∑jx=ix
选到一个关卡一定能通关,花费一小时
求合理分块的情况下,通关所有关卡块的期望时间最小是多少
几个月前做过一次这道题,当时比较弱,不会推期望公式
很显然地能得到一个dp转移方程,设
dp[k][i]
为前
i
位分成
dp[k][i]=min(dp[k−1][j]+E(j+1,i))
其中
E(j+1,i)
为
[j+1,i]
这一块的通关期望
先研究
E(l,r)
的表达式,显然我们有
E(l,i−1)=pE(l,i)+(1−p)E(l,i−1)+1
其中
p
为开启新的关卡的概率,
所以
E(l,i−1)=E(l,i)+1p
,这样一来就可以得到
E(l,r)=∑i=lr∑ij=ltjti
为了能
(1)
得到
E(l,r)
,我们对这个式子变形一下,并预处理出一些前缀和
S[i]
表示
ti
的前缀和,
A[i]
表示
S[i]ti
的前缀和,
B[i]
表示
1ti
的前缀和
E(l,r)=∑i=lrS[i]−S[l−1]ti
=∑i=lrS[i]ti−S[l−1]×∑i=lr1ti
=A[r]−A[l−1]−S[l−1]×(B[r]−B[l−1])
所以
dp[k][i]=min(dp[k−1][j]+A[i]−A[j]−S[j]×(B[i]−B[j]))
至此我们得到了完整的转移方程,时间复杂度为
(N2K)
但是这个复杂度显然不能通过
N≤2×105
的数据,所以考虑优化(斜率优化)
斜率优化本质上就是一个利用单调队列维护下凸壳的过程,
一些形如
dp[i]=min(dp[j]+W(j,i))
的方程都可能用得上这个优化
设下标
k<j
,并且
dp[k]+E(k+1,i)>dp[j]+E(j+1,i)
,那么显然我们选择从
j
转移
展开这个式子,能得到
化简成如下形式
(dp[j]−A[j]+S[j]×B[j])−(dp[k]−A[k]+S[k]×B[k])S[j]−S[k]<B[i]
左边就变成了一个斜率的形式,而右边的
B[i]
是单调递增的
也就是说所有斜率小于
B[i]
的
k
都能被优化掉,
而优化掉之后剩下能转移的点就形成了一个下凸壳
利用单调队列维护这个凸壳,每个点最多只会进队出队一次
这样一来时间复杂度就是
#pragma comment(linker, "/STACK:102400000,102400000")
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <string>
#include <complex>
using namespace std;
typedef pair<int,int> Pii;
typedef long long LL;
typedef unsigned long long ULL;
typedef double DBL;
typedef long double LDBL;
#define MST(a,b) memset(a,b,sizeof(a))
#define CLR(a) MST(a,0)
#define SQR(a) ((a)*(a))
#define PCUT puts("\n----------")
const int maxn=2e5+10;
int N,K,T[maxn];
DBL A[maxn],B[maxn],S[maxn],dp[2][maxn];
int que[2*maxn];
DBL Y(int,int,int), X(int,int);
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
while(~scanf("%d%d", &N, &K))
{
for(int i=1; i<=N; i++)
{
scanf("%d", &T[i]);
S[i] = S[i-1] + T[i];
A[i] = A[i-1] + S[i]/T[i];
B[i] = B[i-1] + 1.0/T[i];
dp[1][i] = A[i];
}
// for(int i=1; i<=N; i++) printf("%.4f\n", A[i]);
for(int k=2,cur,las,head,tail; k<=K; k++)
{
head=0,tail=0;
cur = k&1, las = (k-1)&1;
que[tail++] = 0;
CLR(dp[cur]);
for(int i=1; i<=N; i++)
{
while(tail-head>1 && Y(las,que[head],que[head+1]) < X(que[head],que[head+1])*B[i]) head++;
int p = que[head];
dp[cur][i] = dp[las][p] + A[i] - A[p] - S[p]*(B[i]-B[p]);
while(tail-head>1 &&
Y(las,que[tail-1],i)*X(que[tail-2],que[tail-1]) - Y(las,que[tail-2],que[tail-1])*X(que[tail-1],i) <= 0) tail--;
que[tail++] = i;
}
}
printf("%.6f\n", dp[K&1][N]);
}
return 0;
}
DBL Y(int cur, int k, int j)
{
return (dp[cur][j]-A[j]+S[j]*B[j])-(dp[cur][k]-A[k]+S[k]*B[k]);
}
DBL X(int k, int j)
{
return S[j]-S[k];
}