http://codeforces.com/problemset/problem/643/C
题意:给你n个数字ti,让你分成m段,然后每个数初始都是未标记的,然后你每次选择第一个存在未标记数字的段,然后把这段里面已经标记的数字求和,然后把第一个未标记的数字也加入和里面,然后开始取数字,取到未标记的这个数字,然后把它标记,概率pi=sum/ti
然后让你分段,求出最少需要取的次数的期望。
题解:题目比较绕,但是仔细看就发现了,其实都是按照数的顺序取的,然后取每个数的概率为pi,所以期望就是1/pi。
假设在一段[l,r]中,ti的概率pi=ti/sigma(tk) (l<=k<=i),期望ei=sigma(tk)/ti
设dp[i][j]为前i个数分成j段,最小的期望
转移为:dp[i][j]=min(dp[k][j-1]+cost(k+1,i));
cost(k+1,i)=sigma(e[x]) (k+1<=x<=i)即k+1到i分成了一段
这就是斜率优化dp的模型了,只要能够O(1)的求出cost,就能O(nm)的复杂度内求出答案
cost怎么求呢,可以用三个数组来维护,sum1记录1/ti的和,sum2记录ti的和,sum3记录sum2/ti的和,然后cost(j+1,i)=sum3[i]-sum3[j]-sum2[j]*(sum1[i]-sum1[j])
然后就是正常斜率优化的套路了,这里就不赘述了,等会写个斜率优化的学习专题
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> #pragma comment(linker,"/STACK:102400000,102400000") using namespace std; #define MAX 200005 #define MAXN 1000005 #define maxnode 10 #define sigma_size 2 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lrt rt<<1 #define rrt rt<<1|1 #define middle int m=(r+l)>>1 #define LL long long #define ull unsigned long long #define mem(x,v) memset(x,v,sizeof(x)) #define lowbit(x) (x&-x) #define pii pair<int,int> #define bits(a) __builtin_popcount(a) #define mk make_pair #define limit 10000 //const int prime = 999983; const int INF = 0x3f3f3f3f; const LL INFF = 0x3f3f; const double pi = acos(-1.0); const double inf = 1e18; const double eps = 1e-9; const LL mod = 1e9+7; const ull mxx = 1333331; /*****************************************************/ inline void RI(int &x) { char c; while((c=getchar())<'0' || c>'9'); x=c-'0'; while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0'; } /*****************************************************/ double sum1[MAX]; double sum2[MAX]; double sum3[MAX]; double dp[MAX][55]; int q[MAX]; double getup(int j,int k,int x){ return dp[j][x-1]-sum3[j]+sum2[j]*sum1[j]-(dp[k][x-1]-sum3[k]+sum2[k]*sum1[k]); } double getdown(int j,int k,int x){ return sum2[j]-sum2[k]; } double getdp(int i,int j,int x){ return dp[j][x-1]+sum3[i]-sum3[j]-sum2[j]*(sum1[i]-sum1[j]); } int main(){ //freopen("in.txt","r",stdin); int n,m; while(cin>>n>>m){ sum1[0]=sum2[0]=sum3[0]=0; for(int i=1;i<=n;i++){ int a; scanf("%d",&a); sum1[i]=sum1[i-1]+1.0/a; sum2[i]=sum2[i-1]+a; sum3[i]=sum3[i-1]+sum2[i]/a; //cout<<sum1[i]<<" "<<sum2[i]<<" "<<sum3[i]<<endl; } for(int i=1;i<=n;i++){ dp[i][1]=sum3[i]; //cout<<sum3[i]<<endl; } //cout<<sum3[4]-sum3[1]-sum2[1]*(sum1[4]-sum1[1])<<endl; for(int j=2;j<=m;j++){ int head=0,tail=0; q[tail++]=j-1; for(int i=j;i<=n;i++){ while(tail>head+1&&getup(q[head+1],q[head],j)<=sum1[i]*getdown(q[head+1],q[head],j)) head++; dp[i][j]=getdp(i,q[head],j); //cout<<dp[i][j]<<" "<<i<<" "<<j<<endl; while(tail>head+1&&getup(i,q[tail-1],j)*getdown(q[tail-1],q[tail-2],j)<=getup(q[tail-1],q[tail-2],j)*getdown(i,q[tail-1],j)) tail--; q[tail++]=i; } } printf("%.10f\n",dp[n][m]); } return 0; }