Codeforces 643C Levels and Regions(斜率优化dp)



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;
}

你可能感兴趣的:(Codeforces 643C Levels and Regions(斜率优化dp))