peiwenjun's blog 没有知识的荒原

CF377E Cookie Clicker 题解

题目描述

\(n\) 个工厂,建造第 \(i\) 个工厂需要花费 \(c_i\) 枚金币,建造以后每秒会产出 \(v_i\) 枚金币。

你初始没有金币,每一秒你可以选择一个已经建造的工厂并收集(这一秒)它产出的金币,我们认为建造工厂不需要花费时间。

求至少拥有 \(s\) 枚金币的最小时间。

数据范围

  • \(1\le n\le 2\cdot 10^5,1\le v_i\le 10^8,0\le c_i\le 10^8,1\le s\le 10^{16}\) ,保证至少存在一个 \(c_i\) 为零。

时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{256MB}\)

分析

如果 \(v_i\le v_j,c_i\ge c_j\) ,那么我们一定不会建造第 \(i\) 个工厂。

因此剩下的工厂关于 \(v_i,c_i\) 分别递增,并且我们只会从左往右建造工厂。

注意到最优策略一定是先花费最少时间建造某个工厂,然后仅在这个工厂收集金币,直到金币数量为 \(s\)

\(f_i\) 表示建造第 \(i\) 座工厂, \(\big(\) 建造时间 \(\times v_i-\) 剩余金币数 \(\big)\) 的最小值。

那么第 \(x\) 秒末的金币数量为 \(y=v_i\cdot x-f_i\) ,答案为 \(\min\limits_{1\le i\le n}\lceil\frac{s+f_i}{v_i}\rceil\)

\(x=\lceil\frac{f_j+c_i}{v_j}\rceil\) ,则转移代价为 \(v_j\cdot x-(v_i\cdot x-f_j-c_i)\) ,即:

\[f_i=\min_{1\le j<i}\big((v_i-v_j)\cdot\lceil\frac{f_j+c_i}{v_j}\rceil+f_j+c_i\big)\\ \]

暴力转移时间复杂度 \(\mathcal O(n^2)\) ,但是打表发现 \(f\) 满足决策单调性,使用二分队列优化即可做到 \(\mathcal O(n\log n)\)

#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define mp make_pair
#define pii pair<int,int>
using namespace std;
const int maxn=2e5+5,inf=1e18;
int h,m,n,s,t,res=inf;
pii p[maxn];
int c[maxn],f[maxn],v[maxn];
struct node
{
    int x,l,r;
}q[maxn];
inline bool cmp(pii a,pii b)
{
    if(a.fi!=b.fi) return a.fi<b.fi;
    return a.se>b.se;
}
inline int get(int a,int b)
{
    return (a+b-1)/b;
}
inline int calc(int j,int i)
{
    int x=get(f[j]+c[i],v[j]);
    return (v[i]-v[j])*x+f[j]+c[i];
}
signed main()
{
    scanf("%lld%lld",&n,&s);
    for(int i=1;i<=n;i++) scanf("%lld%lld",&p[i].fi,&p[i].se);
    sort(p+1,p+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        while(m&&p[i].se<=p[m].se) m--;
        p[++m]=p[i];
    }
    for(int i=1;i<=m;i++) v[i]=p[i].fi,c[i]=p[i].se;
    q[h=t=1]={1,2,m};
    for(int i=2;i<=m;i++)
    {
        while(h<t&&q[h].r<i) h++;
        f[i]=calc(q[h].x,i);
        while(h<t&&calc(q[t].x,q[t].l)>=calc(i,q[t].l)) t--;
        int l=max(i,q[t].l-1),r=q[t].r+1;
        while(r-l>1)
        {
            int mid=(l+r)>>1;
            calc(q[t].x,mid)>=calc(i,mid)?r=mid:l=mid;
        }
        if(r<=m) q[t].r=r-1,q[++t]={i,r,m};
    }
    for(int i=1;i<=m;i++) res=min(res,get(f[i]+s,v[i]));
    printf("%lld\n",res);
    return 0;
}

posted on 2023-05-31 15:10  peiwenjun  阅读(9)  评论(0)    收藏  举报

导航