首页 > 其他 > 详细

[CEOI 2004]锯木厂选址

时间:2020-02-05 01:33:22      阅读:62      评论:0      收藏:0      [点我收藏+]

Description

题库链接

从山顶上到山底下沿着一条直线种植了 \(n\) 棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。

木材只能朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建这两个锯木厂,使得运输的费用总和最小。假定运输每公斤木材每米需要一分钱。询问计算最小运输费用。

\(1\leq n\leq 20000\)

Solution

记从山顶向下重量做一个前缀和为 \(w_i\),第 \(i\) 棵树到山底的距离为 \(d_i\),所有树到底端的距离乘重量的和为 \(sum\)

枚举两个锯木厂位置 \(i,j(j<i)\)。显然答案就是 \(\max\{sum-w_j\times d_j-(w_i-w_j)\times d_i\}\)

由于 \(w\)\(d\) 的单调性相反,于是这个式子是可以斜率优化的,把 \(j\) 优化掉就可以 \(O(n)\) 求解了。

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 20000+5;

int n, w[N], d[N], f[N], q[N], head, tail, tmp, ans;

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d%d", &w[i], &d[i]);
    for (int i = n; i >= 1; i--) d[i] += d[i+1], tmp += w[i]*d[i];
    for (int i = 1; i <= n; i++) w[i] += w[i-1];
    q[head = tail = 1] = 1;
    for (int i = 2; i <= n; i++) {
        while (head < tail && 1ll*w[q[head+1]]*d[q[head+1]]-w[q[head]]*d[q[head]]
         >= 1ll*d[i]*(w[q[head+1]]-w[q[head]])) ++head;
        ans = max(ans, w[q[head]]*d[q[head]]-d[i]*w[q[head]]+w[i]*d[i]);
        while (head < tail && 1ll*(w[i]*d[i]-w[q[tail-1]]*d[q[tail-1]])*(w[i]-w[q[tail]])
         <= 1ll*(w[i]*d[i]-w[q[tail]]*d[q[tail]])*(w[i]-w[q[tail-1]])) --tail;
        q[++tail] = i;
    }
    printf("%d\n", tmp-ans);
    return 0;
}

[CEOI 2004]锯木厂选址

原文:https://www.cnblogs.com/NaVi-Awson/p/12262130.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!