LG4360 [CEOI2004]锯木厂选址
题意
原题来自:CEOI 2004
从山顶上到山底下沿着一条直线种植了 n 棵老树。当地的政府决定把他们砍下来。为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂。
木材只能朝山下运。山脚下有一个锯木厂。另外两个锯木厂将新修建在山路上。你必须决定在哪里修建这两个锯木厂,使得运输的费用总和最小。假定运输每公斤木材每米需要一分钱。
你的任务是编写一个程序,读入树的个数和他们的重量与位置,计算最小运输费用。
\(n \leq 2 \times 10^5\)
分析
参照TimeTraveller的题解。这题是斜率优化,不过算不上dp。
我们先写出朴素的DP方程式:
\[
dp[i]=\min\{ totsum-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j]) \}(j<i)
\]
其中\(dp[i]\)表示当前第二个工厂修到第ii棵树的位置时的最小花费,\(totsum\)表示所有树一开始全部运送的山脚下的花费,\(dis[i]\)表示距离的后缀和(因为我们是从上运到下面),\(sum[i]\)表示树的重量的前缀和。那么在\(i,j\)处修了工厂后花费就变成了总花费\(totsum\)减去从\(j\)厂运到山脚的额外花费\(dis[j]*sum[j]\),再减去从\(i\)厂运到山脚下的额外花费\(dis[i]∗(sum[i]−sum[j])\)。
形象的说,就是你先把\(j\)前面的木材运到\(j\)厂,然后减去这些木材运到山脚的花费,再把\(i,j\)之间的木材运到\(i\)厂,再减去它们到山脚的花费。
然后我们将DP方程式变形,令\(j,k(j<k)\)这两种决策转移到\(i\)的时候,\(k\)决策更优秀,那么就可以得到
\[
totsum-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j])>totsum-dis[k]*sum[k]-dis[i]*(sum[i]-sum[k])
\]
整理后可以得出:
\[
\frac{dis[j]*sum[j]-dis[k]*sum[k]}{sum[j]-sum[k]}>dis[i]
\]
然后根据\(>\)号,对非上凸的情况分类讨论可以得出要维护上凸包。讨论过程如下:
首先假设有不上凸的连续\(i,j,k\)三个点,\(k_{i,j}<k_{j,k}\)。有三种情况
- \[
k_{i,j}<k_{j,k}<dis
\]
那么\(i\)比\(j\)优,\(j\)比\(k\)优。 - \[
k_{i,j}<dis<k_{j,k}
\]
那么\(i\)比\(j\)优,\(k\)比\(j\)优。 - \[
dis<k_{i,j}<k_{j,k}
\]
那么\(j\)比\(i\)优,\(k\)比\(j\)优。
综上,\(j\)是无用的点。所以要维护上凸性,就是上凸包。
至此,得出一般结论:
- 若斜率式后的符号是\(>\),则维护上凸包。
- 若斜率式后的符号是\(<\),则维护下凸包。
然后由于\(dis[i]\)单调递减,所以可以用单调队列优化。
时间复杂度\(O(n)\)。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<bitset>
#include<cassert>
#include<ctime>
#include<cstring>
#define rg register
#define il inline
#define co const
template<class T>il T read()
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return data*w;
}
template<class T>T read(T&x)
{
return x=read<T>();
}
using namespace std;
typedef long long ll;
co int N=2e5+2;
ll w[N],d[N];
ll sum;
int q[N];
ll Up(int j,int k) // edit 1: long long for slope
{
return d[j]*w[j]-d[k]*w[k];
}
ll Down(int j,int k)
{
return w[j]-w[k];
}
ll Cal(int i,int j)
{
return sum-d[j]*w[j]-d[i]*(w[i]-w[j]);
}
int main()
{
// freopen("LG4360.in","r",stdin);
// freopen(".out","w",stdout);
int n=read<int>();
for(int i=1;i<=n;++i)
read(w[i]),read(d[i]);
for(int i=n;i>=1;--i)
d[i]+=d[i+1],sum+=w[i]*d[i];
for(int i=1;i<=n;++i)
w[i]+=w[i-1];
int head=0,tail=0;
q[tail++]=0;
ll ans=sum;
for(int i=1;i<=n;++i)
{
while(head+1<tail&&Up(q[head+1],q[head])>=d[i]*Down(q[head+1],q[head]))
++head;
ans=min(ans,Cal(i,q[head]));
while(head+1<tail&&Up(i,q[tail-1])*Down(q[tail-1],q[tail-2])>=Up(q[tail-1],q[tail-2])*Down(i,q[tail-1]))
--tail;
q[tail++]=i;
}
printf("%lld\n",ans);
return 0;
}
LG4360 [CEOI2004]锯木厂选址的更多相关文章
- P4360 [CEOI2004]锯木厂选址
P4360 [CEOI2004]锯木厂选址 这™连dp都不是 \(f_i\)表示第二个锯木厂设在\(i\)的最小代价 枚举1号锯木厂 \(f_i=min_{0<=j<i}(\sum_{i= ...
- luoguP4360 [CEOI2004]锯木厂选址
题目链接 luoguP4360 [CEOI2004]锯木厂选址 题解 dis:后缀和 sum:前缀和 补集转化,减去少走的,得到转移方程 dp[i] = min(tot - sumj * disj - ...
- 动态规划(斜率优化):[CEOI2004]锯木厂选址
锯木场选址(CEOI2004) 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂. 木材只能按照一个方向运输:朝山下运.山脚下有 ...
- [BZOJ2684][CEOI2004]锯木厂选址
BZOJ权限题! Description 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来.为了不浪费任何一棵木材,树被砍倒后要运送到锯木厂. 木材只能按照一个方向运输:朝山下运 ...
- cogs 362. [CEOI2004]锯木厂选址
★★★ 输入文件:two.in 输出文件:two.out 简单对比 时间限制:0.1 s 内存限制:32 MB 从山顶上到山底下沿着一条直线种植了n棵老树.当地的政府决定把他们砍下来. ...
- 2018.08.28 洛谷P4360 [CEOI2004]锯木厂选址(斜率优化dp)
传送门 一道斜率优化dp入门题. 是这样的没错... 我们用dis[i]表示i到第三个锯木厂的距离,sum[i]表示前i棵树的总重量,w[i]为第i棵树的重量,于是发现如果令第一个锯木厂地址为i,第二 ...
- 洛谷P4360 [CEOI2004]锯木厂选址(斜率优化)
传送门 我可能根本就没有学过斜率优化…… 我们设$dis[i]$表示第$i$棵树到山脚的距离,$sum[i]$表示$w$的前缀和,$tot$表示所有树运到山脚所需要的花费,$dp[i]$表示将第二个锯 ...
- luogu P4360 [CEOI2004]锯木厂选址
斜率优化dp板子题[迫真] 这里从下往上标记\(1-n\)号点 记\(a_i\)表示前缀\(i\)里面树木的总重量,\(l_i\)表示\(i\)到最下面的距离,\(s_i\)表示\(1\)到\(i-1 ...
- [CEOI2004]锯木厂选址 斜率优化DP
斜率优化DP 先考虑朴素DP方程, f[i][k]代表第k个厂建在i棵树那里的最小代价,最后答案为f[n+1][3]; f[i][k]=min(f[j][k-1] + 把j+1~i的树都运到i的代价) ...
随机推荐
- gvim中对变量的识别
最近在项目中使用gvim打开一个文件,发现对某个变量不识别. 后来发现是gvim中对{$comm_ver},带花括号的变量不识别. 类似这样:parameter memory_spec = " ...
- ubuntu 刚更改默认python3版本后更新包等
一般来说ubuntu 刚更改为python3为默认版本后要进行一下更新包等等的内容(当然不更新一下也是可以的,最好更新一下,第一次更新较慢) 使用下面两行代码: sudo apt-get update ...
- HTML5/CSS3鼠标悬停动画菜单按钮
在线演示 本地下载
- 行列转换文本处理--awk xargs 回顾
awk 数组回顾: 9.1 数组 举例:统计当前主机上每一个TCP连接状态以及每种连接状态的数目[非常实用] # netstat -tan | awk '/^tcp/{STATE[$NF]++}END ...
- java判断集合list是为空
if(null == list || list.size() ==0 ){ } list.isEmpty()和list.size()==0 没有区别 isEmpty()判断有没有元素而size()返回 ...
- 史上最详细Windows版本搭建安装React Native环境配置
说在前面的话: 感谢同事金晓冰倾情奉献本环境搭建教程 之前我们已经讲解了React Native的OS X系统的环境搭建以及配置,鉴于各大群里有很多人反应在Windows环境搭建出现各种问题,今天就特 ...
- spring核心技术IOC,DI
在介绍Spring中IOC之前,先说说Spring的优势有哪些.首先1.方便解耦,简化开发:就是说将对象间的依赖关系交给Spring进行控制,避免了编码造成的过度耦合.2.AOP编程的支持:方便进行切 ...
- JavaScript字符串转换为变量名
1.将一个字符串转换为变量名 [javascript] view plain copy print? function string_to_name(string){ let _name = 'var ...
- Springboot- Spring缓存抽象学习笔记
Spring缓存作用准备: 1.准备数据(准备一个有数据的库和表/导入数据库文件,准备好表和表里面的数据) 2.创建javaBean封装数据 3.整合MyBatis操作数据库( 这里用MyBatis) ...
- centos下tomcat自启动
一.在指定目录创建脚本并赋予755权限 vim /etc/init.d/tomcat #!/bin/bash # # kenny kenny.zhou@tom.com # /etc/rc.d/init ...