1 条题解

  • 0
    @ 2021-11-19 8:58:14

    一步步分析这个题目。

    首先显然的,砍一个木头必须全部砍完,不然必然不优。

    由于 bb 满足单调下降,因此一定是从小到大选择一些木头去砍,直到砍掉第 nn 根木头,这样我砍木头就都是免费的了,就可以砍掉剩余的木头。

    砍树必须从小到大砍,否则如果我砍了一个 ii,然后砍一个 jj 满足 i>ji>j,那么 jj 对后面没有任何贡献,不如砍后面的再来砍 jj

    比较容易想到一个 O(n2)O(n^2) 的暴力 dp。

    fif_i 表示砍到第 ii 根木头所需要的最小代价,容易得到状态转移方程。

    $$f_1=b_1 \\ f_i=\min_{j=1}^{i-1} f_j+b_j\times (a_i-1)+b_i $$

    容易发现这个东西可以斜率优化。

    假设对于 j<kj<kjjkk 优。

    $$f_j+b_j\times(a_i-1)<f_k+b_k\times(a_i-1)\\ a_i-1<\frac{f_k-f_j}{b_j-b_k} $$

    直接用单调队列维护,时间复杂度 O(n)O(n)


    代码
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    int n,a[N],b[N],q[N],head,tail;
    long long f[N];
    double F(int x,int y){return 1.0*(f[y]-f[x])/(b[x]-b[y]);}
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    	f[1]=b[1];
    	head=tail=1;
    	q[1]=1;
    	for(int i=2;i<=n;i++){
    		while(head<tail&&F(q[head],q[head+1])<a[i]-1)head++;
    		f[i]=f[q[head]]+1ll*(a[i]-1)*b[q[head]]+b[i];
    		while(head<tail&&F(q[tail-1],q[tail])>F(q[tail],i))tail--;
    		q[++tail]=i;
    	}
    	printf("%lld",f[n]);
    }
    
    • 1

    信息

    ID
    12
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者