zoukankan      html  css  js  c++  java
  • 数学

    数学

    时间限制: 5000 ms 内存限制: 131072 KB

    题目描述

    Shy 有一个长度为 n 的数组 a[i],让你把这个数组分成 k 份(每份包含至少一个元素,且每份中的元素连续)。假设其中一份长这个样子,b[1], b[2],…,b[m],那么他的价值是b[1]/b[1]+(b[1]+b[2])/b[2]+…+(b[1]+b[2]+…+b[m])/b[m],求在所有的合法的划分中,最小的价值和是多少。

    输入

    第一行两个整数 n,k 表示数组长度和分成的份数;
    第二行为n个整数。

    输出

    输出一个数表示答案。(要求与标准答案的绝对或相对误差不差过 1/10000)。

    输入样例

    4 2
    100 3 5 7

    输出样例

    5.7428571429

    数据规模

    对于 30%的数据,1≤n≤1000;
    对于 100%的数据,1≤n≤200000,1≤k≤min(50,n),1≤a[i]≤100000。

    这是一道斜率优化。。。。
    我个人觉得w大爷会毒瘤学弟学妹,所以记一下。。。。
    疯狂推公式吧。。。
    正确姿势如下:

    写方程

    凑乘法

    找单调

    为了完成上面的操作,疯狂预处理吧!(遇到求和就预处理233)

    
    #include<bits/stdc++.h>
    using namespace std;
    struct lpl{
    	double x, y;
    }lin;
    const int maxn = 2e5 + 5;
    double R[maxn], D[maxn], S[maxn], dp[55][maxn];
    int n, k, ini[maxn], l[55], r[55];
    lpl getans[55][maxn];
    
    inline double slop(lpl A, lpl B) {return (double)(A.y - B.y) / (A.x - B.x);}
    
    inline void putit()
    {
    	scanf("%d%d", &n, &k); 
    	for(int i = 1; i <= n; ++i) scanf("%d", &ini[i]);
    	for(int i = 1; i <= n; ++i){
    		S[i] = S[i - 1] + ini[i];
    		R[i] = R[i - 1] + S[i] / ini[i];
    		D[i] = D[i - 1] + (double)(1.0) / ini[i];
    	}
    }
    
    inline void workk()
    {
    	for(int i = 1; i <= k; ++i) l[i] = 1;
    	for(int i = 1; i <= n; ++i){
    		dp[1][i] = dp[1][i - 1] + S[i] / ini[i];	
    		lin.x = S[i]; lin.y = dp[1][i] + D[i] * S[i] - R[i];
    		while(l[1] < r[1] && slop(getans[1][r[1] - 1], getans[1][r[1]]) > slop(lin, getans[1][r[1]])) r[1]--;
    		r[1]++; getans[1][r[1]] = lin; 
    	}
    	for(int i = 2; i <= k; ++i){
    		for(int j = i; j <= n; ++j){
    			while(l[i - 1] < r[i - 1] && slop(getans[i - 1][l[i - 1]], getans[i - 1][l[i - 1] + 1]) < D[j]) l[i - 1]++;
    			dp[i][j] = getans[i - 1][l[i - 1]].y - D[j] * getans[i - 1][l[i - 1]].x + R[j];
    			lin.x = S[j]; lin.y = dp[i][j] + D[j] * S[j] - R[j];
    			while(l[i] < r[i] && slop(getans[i][r[i] - 1], getans[i][r[i]]) > slop(lin, getans[i][r[i]])) r[i]--;
    			r[i]++; getans[i][r[i]] = lin;
    		}		
    	}
    
    }
    
    int main()
    {
    	putit();
    	workk();
    	printf("%.10lf", dp[k][n]);
    	return 0;
    }
    
    
    心如花木,向阳而生。
  • 相关阅读:
    CSUSTOJ-伊井野弥子是风纪委员(简单BFS)
    CSUSTOJ-石上优不想留级(一维坐标三分及思维解法)
    CSUSTOJ-哈希的纸团(mid思维)
    CSUSTOJ-辉夜大小姐想被猜中(简单暴力)
    CSUSTOJ-藤原书记想要探病(简单矩阵快速幂)
    CSUSTOJ-石上优想要逃离(STL+思维暴力)
    CSUSTOJ-白银御行想展示(思维题)
    CSUSTOJ-藤原书记的佩斯(简单数学)
    CSUSTOJ-白银探病篇(简单思维)
    Odoo发邮件被服务器退回
  • 原文地址:https://www.cnblogs.com/LLppdd/p/8946813.html
Copyright © 2011-2022 走看看