斜率优化技巧——换个角度思考

[bzoj3437]小P的牧场

试题描述

背景

    小P是个特么喜欢玩MC的孩纸。。。

 描述

    小P在MC里有n个牧场,自西向东呈一字形排列(自西向东用1…n编号),于是他就烦恼了:为了控制这n个牧场,他需要在某些牧场上面建立控制站,每个牧场上只能建立一个控制站,每个控制站控制的牧场是它所在的牧场一直到它西边第一个控制站的所有牧场(它西边第一个控制站所在的牧场不被控制)(如果它西边不存在控制站,那么它控制西边所有的牧场),每个牧场被控制都需要一定的花费(毕竟在控制站到牧场间修建道路是需要资源的嘛~),而且该花费等于它到控制它的控制站之间的牧场数目(不包括自身,但包括控制站所在牧场)乘上该牧场的放养量,在第i个牧场建立控制站的花费是ai,每个牧场i的放养量是bi,理所当然,小P需要总花费最小,但是小P的智商有点不够用了,所以这个最小总花费就由你来算出啦。

输入

  第一行一个整数 n 表示牧场数目

  第二行包括n个整数,第i个整数表示ai

  第三行包括n个整数,第i个整数表示bi

输出

  只有一行,包括一个整数,表示最小花费

输入示例

4
2 4 2 4
3 1 4 2

输出示例

9

样例解释

选取牧场1,3,4建立控制站,最小费用为2+(2+1*1)+4=9。

数据范围

对于100%的数据,1<=n<=1000000,0<ai,bi<=10000

题解

最直接的想法是设f(i)表示前i个牧场全部管理所花的最小费用。

转移为f(i) = min{ f(j) + (sum[j+1] - sum[i]) / (n - i + 1) + a[i] | 0 ≤ j < i } 其中,sum[i] = b[i] * (n - i + 1) + b[i+1] * (n - i) + ... + b[n]

好,O(n2)的,斜率优化,捯饬一下不等式(j比k优,j < k)

发现得到了一个这个:(n - i + 1)( f(j) - f(k) ) + sum[j] - sum[k] < 0

怎么处理都不能把左边的i去掉,即,左边的式子总是与i有关,就不能用斜率优化了。

所以我们需要换一个角度,倒着想。

不难计算出只在第n个牧场放一个控制站的费用,再在此基础上减去最大的“省去的费用”即可。

设f(i)表示考虑 i~n 个牧场,并且在第i个牧场放控制站的最大的“省去的费用”。

转移为f(i) = max{ f(j) + (j - i) · S[i] | i < j < n } - a[i] 其中S[i]表示数组b的前缀和

令 j < k 且 j比k优

f(j) + (j - i) * S[i] > f[k] + (k - i) * S[i]

得 ( f(j) - f(k) ) / (k - j) > S[i],这题可做了

TAT又一次被long long坑了。。。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 1000010
#define LL long long
int n, A[maxn], B[maxn], l, r, q[maxn];
LL Sum, S[maxn], f[maxn];

double slop(int j, int k) {
    return (f[j] - f[k]) / (double)(k - j);
}

int main() {
    n = read();
    for(int i = 1; i <= n; i++) A[i] = read();
    for(int i = 1; i <= n; i++) B[i] = read();
    
    for(int i = n, j = 0; i; i--, j++) Sum += (LL)B[i] * j;
    LL ans = (LL)A[n] + Sum, tmp = 0;
    for(int i = 1; i <= n; i++) S[i] = S[i-1] + B[i];
    l = 1; r = 0; q[++r] = n;
    for(int i = n - 1; i; i--) {
        while(l < r && slop(q[l], q[l+1]) > S[i]) l++;
        f[i] = f[q[l]] + (q[l] - i) * S[i] - A[i];
        tmp = max(tmp, f[i]);
        while(l < r && slop(i, q[r]) > slop(q[r-1], q[r])) r--;
        q[++r] = i;
    }
    
    printf("%lld\n", ans - tmp);
    
    return 0;
}
这儿有你要的代码!

 

posted @ 2016-01-21 20:16  xjr01  阅读(382)  评论(0编辑  收藏  举报