• 【题解】[CEOI2004]锯木厂选址


    Link

    ( ext{Solution:})

    注意到题目中的编号是倒着的,于是我们的距离要预处理的是后缀和。

    考虑如何(n^2)搞:

    (dp[i])表示选择(i)为第二个中转点的最小代价。

    枚举在(i)前面的(j),代价就是(dp[i]=min_{j<i}All-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j]))

    (All)是所有树木运输到(1)号点的代价。可以理解为,有一部分运输到(j)就不用运了,于是把这部分减掉。(sum)是重量的前缀和。

    枚举前一个点,显然是(n^2)的(虽然这样可以过)

    考虑优化,先推柿子(令(S=All)):

    [dp[i]=S-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j]) ]

    [dis[j]*sum[j]=S-dp[i]-dis[i]*sum[i]+dis[i]*sum[j] ]

    [dis[j]*sum[j]=dis[i]*sum[j]+(S-dp[i]-dis[i]*sum[i]) ]

    这时,令:(y=dis[j]*sum[j],k=dis[i],x=sum[j],b=(S-dp[i]-dis[i]*sum[i])),一个一次函数式出来了。

    显然的斜率优化,但是这里有一个坑,害得我看博客又理解了半天……

    其实,如果按照最小化截距来写,会发现这就是一个下凸壳,即使(dis[i])是递减而非递增。但是看了题解后发现,维护的是一个上凸壳。

    为什么?

    考虑我们究竟要最小化还是最大化。

    如果最小化(b=(S-dp[i]-dis[i]*sum[i])),则因为(dp[i])前面的符号是负的,所以我们就反其道而行之地把它给最大化了。于是( ext{Wrong Answer.})

    所以,实际上我们要最大化截距(b),从而最小化(dp[i]).

    于是我们的任务就变成维护一个上凸包了。观察到(dis[i])递减,于是我们只保留小于(dis[i])的线段。因为后面的最优线段一定是斜率递减的。

    我们通过上述分析,可以通过单调队列优化到(O(n).)

    这题的主要价值在于,注意(b)的符号,不是题目中要求(min)就一定是下凸包,也不是题目中求最大值就一定是下凸包。看截距的时候要特别留意(dp[i])——我们最关心的值的符号,以此来确定维护上凸壳还是下凸壳。

    #include<bits/stdc++.h>
    using namespace std;
    int n,w[200010],d[200010];
    int s[200010],sum[200010];
    int dp[200010],ans=0;
    int head,tail,q[200010];
    int dis[200010],S,A=2147483647;
    int Y(int x){return dis[x]*sum[x];}
    int X(int x){return sum[x];}
    double slope(int x,int y){return (Y(y)-Y(x))/(X(y)-X(x));} 
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)scanf("%d%d",&w[i],&dis[i]);
    	for(int i=n;i>=1;--i)dis[i]+=dis[i+1];
    	for(int i=1;i<=n;++i)ans+=dis[i]*w[i];
    	for(int i=1;i<=n;++i)sum[i]=sum[i-1]+w[i];
    	//Dis[i]>(dis[k]*sum[k]-dis[j]*sum[j])/(sum[k]-sum[j])
    	//Dp[i]=S-dis[j]*sum[j]-dis[i]*(sum[i]-sum[j])
    	//Dis[j]*sum[j]=dis[i]*sum[j]+(S-dp[i]-dis[i]*sum[i])
    	//最小化后面一坨 斜率是dis[i]
    	//head=tail=1;q[head]=1;
    	//cout<<ans<<endl;
    	for(int i=1;i<=n;++i){
    		while(head<tail&&slope(q[head],q[head+1])>=dis[i])head++;
    		dp[i]=ans-dis[q[head]]*sum[q[head]]-dis[i]*(sum[i]-sum[q[head]]);
    		//cout<<dp[i]<<" ";
    		A=min(A,dp[i]);
    		while(head<tail&&slope(q[tail-1],q[tail])<=slope(q[tail-1],i))--tail;
    		q[++tail]=i;
    	}
    	printf("%d
    ",A);
    	return 0;
    }
    
  • 相关阅读:
    一个很好的命令行分享网站
    Docker inside Docker 基于 Alpine Linux
    CentOS 下运行Docker 内执行 docker build 命令的简单方法
    CentOS 安装 Harbor的简单过程(仅使用http 未使用https)
    [财务会计] 表外科目
    jira 插件介绍地址
    Linux 下安装nginx的总结 (之前写的有问题))
    Jira 的 数据库备份恢复 简单过程
    Jira 7.2.4简单安装过程
    Tomcat绑定具体IP
  • 原文地址:https://www.cnblogs.com/h-lka/p/12812919.html
Copyright © 2020-2023  润新知