zoukankan      html  css  js  c++  java
  • [BZOJ4518][Sdoi2016]征途

    [BZOJ4518][Sdoi2016]征途

    试题描述

    Pine开始了从S地到T地的征途。

    从S地到T地的路可以划分成n段,相邻两段路的分界点设有休息站。
    Pine计划用m天到达T地。除第m天外,每一天晚上Pine都必须在休息站过夜。所以,一段路必须在同一天中走完。
    Pine希望每一天走的路长度尽可能相近,所以他希望每一天走的路的长度的方差尽可能小。
    帮助Pine求出最小方差是多少。
    设方差是v,可以证明,v×m^2是一个整数。为了避免精度误差,输出结果时输出v×m^2。

    输入

    第一行两个数 n、m。
    第二行 n 个数,表示 n 段路的长度

    输出

    一个数,最小方差乘以 m^2 后的值

    输入示例

    5 2
    1 2 5 8 6

    输出示例

    36

    数据规模及约定

    1≤n≤3000,保证从 S 到 T 的总路程不超过 30000

    题解

    设 f(i, k) 表示考虑前 i 段路分成 k 次的最小方差。则 f(i, k) = min{ f(j, k-1) + (Si - Sj - x)2 },其中 Si 为前 i 段路的总长,x = Sn / m(想一想为什么)。

    开一个滚动数组然后斜率优化就好了(做法类似http://www.cnblogs.com/xiao-ju-ruo-xjr/p/5149405.html这篇博客中“[bzoj3675][Apio2014]序列分割”这道题的做法,不会的话可以看看那道题,还能得双倍经验= =),这里就不推式子了。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char tc = getchar();
    	while(!isdigit(tc)){ tc = getchar(); if(tc == '-') f = -1; }
    	while(isdigit(tc)){ x = x * 10 + tc - '0'; tc = getchar(); }
    	return x * f;
    }
    
    #define maxn 3010
    #define oo 2147483647
    #define LL long long
    int n, m, cur, Q[maxn];
    LL S[maxn], f[maxn][2];
    
    struct Point { LL x, y; } ps[maxn];
    double slop(int a, int b) { return ((double)ps[a].y - ps[b].y) / ((double)ps[a].x - ps[b].x); }
    
    int main() {
    	n = read(); m = read();
    	for(int i = 1; i <= n; i++) S[i] = S[i-1] + read();
    	
    	for(int i = 1; i <= n; i++) f[1][i] = oo;
    	for(int j = 1; j <= m; j++) {
    		int l = 1, r = 0; Q[++r] = 0; ps[0] = (Point){ 0, 0 };
    		for(int i = 1; i <= n; i++) {
    			while(l < r && 2.0 * (double)m * S[i] >= slop(Q[l], Q[l+1])) l++;
    			int k = Q[l];
    			f[i][cur] = f[k][cur^1] + (LL)m * S[k] * S[k] + 2ll * S[k] * S[n] - 2ll * m * S[i] * S[k] + (LL)m * S[i] * S[i] - 2ll * S[i] * S[n];
    			ps[i] = (Point){ S[i], f[i][cur^1] + (LL)m * S[i] * S[i] + 2ll * S[i] * S[n] };
    			while(l < r && slop(Q[r-1], Q[r]) >= slop(Q[r], i)) r--;
    			Q[++r] = i;
    		}
    		cur ^= 1;
    	}
    	
    	printf("%lld
    ", f[n][cur^1] + S[n] * S[n]);
    	
    	return 0;
    }
    
  • 相关阅读:
    python使用消息队列RabbitMq(入门)
    python Condition类(锁)
    python锁
    python多线程的几种情形分析-三种情况
    git基本使用
    python学习笔记之数据类型、字符编码、文件处理
    NOIP2018提高组模拟题(五)
    10.28模拟赛
    差分+树状数组【p4868】Preprefix sum
    线段树【p2706】贪婪大陆
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5554232.html
Copyright © 2011-2022 走看看