zoukankan      html  css  js  c++  java
  • 洛谷$P4040 [AHOI2014/JSOI2014]$宅男计划 贪心

    正解:三分+贪心

    解题报告:

    传送门$QwQ$

    其实很久以前的寒假就考过了,,,但那时候$gql$没有好好落实,就只写了个二分,并没有二分套三分,就只拿到了$70pts$

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define rp(i,x,y) for(register ll i=x;i<=y;++i)
    #define my(i,x,y) for(register ll i=x;i>=y;--i)
    
    const ll N=10000+10;
    ll m,f,n,p[N],s[N],mnv[N*200],mxs,ans,sum[N*200];
    
    inline ll read()
    {
        char ch=getchar();ll x=0;bool y=1;
        while(ch!='-' && (ch>'9' || ch<'0'))ch=getchar();
        if(ch=='-')ch=getchar(),y=0;
        while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar();
        return y?x:-x;
    }
    void fd(ll v,ll tot)
    {
        ll l=0,r=mxs;
        while(l<r)
        {
            int mid=(l+r+1)>>1;
            if((ll)sum[mid]*tot<=(ll)v)l=mid;
            else r=mid-1;
        }
        ans=max((ll)ans,l*tot+(v-sum[l]*tot)/mnv[l+1]);
    }
    void work()
    {
        for(register ll i=f,tot=1;i<=m;i+=f,++tot)fd(m-i,tot);
        printf("%lld
    ",ans);
    }
    
    int main()
    {
    //    freopen("food.in","r",stdin);
    //    freopen("food.out","w",stdout);
        m=read();f=read();n=read();
        memset(mnv,127/3,sizeof(mnv));mxs=ans=0;
        rp(i,1,n){p[i]=read();s[i]=read()+1;mnv[s[i]]=min(mnv[s[i]],p[i]);mxs=max(s[i],mxs);}
        my(i,mxs-1,1)mnv[i]=min(mnv[i+1],mnv[i]);rp(i,1,mxs)sum[i]=sum[i-1]+mnv[i];
        if(f==0){printf("%lld
    ",m/mnv[1]);return 0;}work();
    }
    View Code

    然后再套个三分就写完了$QwQ$

    还是正儿八经写下题解趴$QwQ$(其实是从,考试总结蒯过来的$QwQ$

    首先显然可以处理一下使得所有物品的花费是随保质期单调增的(单调栈或者直接排序其实都成的$QwQ$

    然后枚举点了几轮外卖,就可以算出外卖费,就可以二分求出可以活几天

    关于二分的正确性很容易证明

    首先我们可以得出对于每一轮的最优天数一定是接近的

    来我证明下

    假如现在有两种方案

    第一种是第一轮买$n$个第二轮买$n+100$个

    第二种是第一轮买$n+50$个第二轮买$n+50$个

    首先前$n$个吃的一定是一样的(显然是先吃花费低保质期低,这个不用证明趴,,,?

    然后第一种第二轮的$n$个之后的那$50$个也一定是和第二种的$n$个之后的那$50$个是一样的

    这样我们的比较就变成了剩下第一种第二轮$n+50$个之后的$50$个第二种第二轮$n$之后的$50$个

    因为我们已经通过处理使得花费随保质期单调增了

    所以买$n+50$个之后的$50$个一定花费不比$n$个之后的$50$个花费少

    所以我们可以证明最优天数要么是$T$要么是$T+1$

    所以我们就可以二分这个$T$,就欧克了

    但是,如果在洛谷上做这道题这个方法就是过不去的了$QAQ$只有$70pts$(好像是$80pts$我写锅了$QwQ$

    说下还要优化哪儿

    就是,看一下上面的解法,会发现唯一可以优化的地方就是,我外卖的轮次是枚举的

    怎么优化呢?三分法

    可以证明,外卖的轮次与存活天数是个二次函数关系

    来下面我再来证明下$QwQ$

    首先如果不收钱,这就是个上凸函数.然后发现要钱了,就减去一个下凸函数

    上凸减下凸依然是上凸,所以这是个上凸函数,可三分$QwQ$

    然后之后的步骤就和上面那个$70pts$的是一样的辣!

    (对了,,,实际应用是要用三分套二分做的$QwQ$.因为二分套三分会爆$ll$好像$kk$

    然后因为三分套二分真的很麻烦$kk$

    所以我写的三分加贪心$QwQ$

    三分加贪心就很$easy$了?三分出外卖的轮数之后先把所有钱减去外卖费用,然后从小往大看每个食物能放多少天,能放就放,$over$

    然后还有就是因为它都贼大所以有一个地方会爆$ll$,,,我的解决方法是把一个数移到右边去除就成$QwQ$,或者写个$lf$快速乘一样的也行$QwQ$

    $over$

     

    #include<bits/stdc++.h>
    using namespace std;
    #define il inline
    #define gc getchar()
    #define ll long long
    #define int long long
    #define ri register int
    #define rb register bool
    #define rc register char
    #define rp(i,x,y) for(ri i=x;i<=y;++i)
    #define my(i,x,y) for(ri i=x;i>=y;--i)
    #define lb(x) lower_bound(st+1,st+st_cnt+1,x)-st
    
    const ll N=200+10;
    ll m,f,n,as;
    struct node{ll p,s;}nod[N];
    
    il ll read()
    {
        rc ch=gc;ll x=0;rb y=1;
        while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
        if(ch=='-')ch=gc,y=0;
        while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
        return y?x:-x;
    }
    il bool cmp(node gd,node gs){return gd.p<gs.p;}
    il ll cal(ll tim)
    {
        ll mon=m-1ll*tim*f,dat=0,ret=0;if(mon<=0)return 0;
        rp(i,1,n)
        {
            ri t=nod[i].s-dat;if(t<=0)continue;
            if(1ll*nod[i].p*t<=mon/tim)ret+=tim*t,mon-=1ll*nod[i].p*tim*t,dat=nod[i].s;
            else return ret+mon/nod[i].p;
        }
        return ret;
    }
    
    signed main()
    {
        //freopen("4040.in","r",stdin);freopen("4040.out","w",stdout);
        m=read();f=read();n=read();rp(i,1,n)nod[i]=(node){read(),read()+1};sort(nod+1,nod+1+n,cmp);
        ll l=1,r=m/f;
        while(r-l>=5)
        {
            ll midl=((l<<1)+r)/3,midr=(l+(r<<1))/3,asl=cal(midl),asr=cal(midr);
            if(asl<=asr)l=midl;;if(asl>=asr)r=midr;
        }
        rp(i,l,r)as=max(as,cal(i));;printf("%lld
    ",as);
        return 0;
    }
    View Code

     

  • 相关阅读:
    C++中 结构体和类的异同
    c++sizeof大全
    10th week task -3 Arrow function restore
    10th week task -2 Object 的起源
    js 的起源故事
    10th week task -1
    9th week -the history of program 1950~2020
    javascript统计一个字符在一段字符串出现次数
    Grid layout
    BOM DOM区别 来源
  • 原文地址:https://www.cnblogs.com/lqsukida/p/11545128.html
Copyright © 2011-2022 走看看