zoukankan      html  css  js  c++  java
  • P4655 [CEOI2017]Building Bridges 题解

    Link

    P4655 [CEOI2017]Building Bridges

    Solve

    我们很容易能退出转移方程

    [dp[i]=min(dp[i],dp[j]+(H[i]-H[j])ast (H[i]-H[j])+S[i-1]-S[j]) ]

    [dp[i]=dp[j]-2ast H[i]ast H[j]+H[j]ast H[j]+H[i]ast [i]+S[i-1]-S[j] ]

    [(2ast H[i]) * H[j] + (dp[i]-S[i-1]-H[i]ast H[i]) = (dp[j]+H[j]ast H[j]-S[j]) ]

    此时就能发现是斜率优化了。

    但是这里的的k[i]和x都不递增,所以用(CDQ)或者线段树来维护,我用的是(CDQ)来维护

    Code

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #define LD long double
    #define LL long long
    #define Re register int
    #define S2(a) (1ll*(a)*(a))
    using namespace std;
    const LL N=1e5+3,inf=1e18;
    int n,H[N],W[N],Q[N];LL S[N],dp[N];
    inline void in(Re &x){
        int f=0;x=0;char c=getchar();
        while(c<'0'||c>'9')f|=c=='-',c=getchar();
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=f?-x:x;
    }
    //dp[i]=min(dp[i],dp[j]+(H[i]-H[j])*(H[i]-H[j])+S[i-1]-S[j]);
    //dp[i]=dp[j]-2*H[i]*H[j]+H[j]*H[j]+H[i]*H[i]+S[i-1]-S[j]
    //(2*H[i]) * H[j] + (dp[i]-S[i-1]-H[i]*H[i]) = (dp[j]+H[j]*H[j]-S[j])
    //   k     *  x   +            b             =          y
    #define X(j) (a[j].x)
    #define Y(j) (a[j].y)
    struct QAQ{
        int k,x,id;LL y;
        inline bool operator<(const QAQ &O)const{return x!=O.x?x<O.x:y<O.y;}
    }a[N],b[N];
    inline bool cmp(QAQ A,QAQ B){return A.k<B.k;}
    inline LD slope(Re i,Re j){return X(i)==X(j)?(Y(j)>Y(i)?inf:-inf):(LD)(Y(j)-Y(i))/(X(j)-X(i));}
    inline void CDQ(Re L,Re R){
        if(L==R){Re j=a[L].id;a[L].y=dp[j]+(LL)H[j]*H[j]-S[j];return;}//此时dp[j]必定已经求出来了,直接算计Y(j)即可
        Re mid=L+R>>1,p1=L,p2=mid+1,h=1,t=0;
        for(Re i=L;i<=R;++i)a[i].id<=mid?b[p1++]=a[i]:b[p2++]=a[i];//按照i的大小分到左右两边(两边的k0[i]分别递增)
        for(Re i=L;i<=R;++i)a[i]=b[i];
        CDQ(L,mid);//处理完左边后,左边的X(j)是递增的,此时右边还没处理,所以右边k0[i]是递增的
        for(Re i=L;i<=mid;++i){//把左边的点拿出来维护凸包(使用单调队列)
            while(h<t&&slope(Q[t-1],Q[t])>=slope(Q[t-1],i))--t;
            Q[++t]=i;
        }
        for(Re i=mid+1,j,id;i<=R;++i){//把右边的点拿来决策(依旧是单调队列)
            while(h<t&&slope(Q[h],Q[h+1])<=a[i].k)++h;
            if(h<=t)id=a[i].id,j=Q[h],dp[id]=min(dp[id],a[j].y-(LL)a[i].k*a[j].x+S[id-1]+(LL)H[id]*H[id]);
        }
        CDQ(mid+1,R);//处理完右边后,两边都按照X(j)排好了序
        Re w=L-1;p1=L,p2=mid+1;//把两边按照X(j)从小到大归并起来
        while(p1<=mid&&p2<=R)b[++w]=a[p1]<a[p2]?a[p1++]:a[p2++];
        while(p1<=mid)b[++w]=a[p1++];while(p2<=R)b[++w]=a[p2++];
        for(Re i=L;i<=R;++i)a[i]=b[i];
    }
    int main(){
    	freopen("P4655.in","r",stdin);
    	freopen("P4655.out","w",stdout);
        in(n);
        for(Re i=1;i<=n;++i)in(H[i]);
        for(Re i=1;i<=n;++i)in(W[i]);
        for(Re i=1;i<=n;++i)S[i]=S[i-1]+W[i],dp[i]=inf;
        for(Re i=1;i<=n;++i)a[i].k=(H[i]<<1),a[i].x=H[i],a[i].id=i;
        sort(a+1,a+n+1,cmp);//先按k0[i]排序
        dp[1]=0,CDQ(1,n);//注意左边界上的点要单独求
        printf("%lld
    ",dp[n]);
    }
    
  • 相关阅读:
    join命令
    参与者模式
    字符串中的第一个唯一字符
    Git与SVN对比
    惰性模式
    .NET Conf 2020
    使用Github部署Azure应用服务
    Azure Terraform(一)入门简介
    打日志还能打出个线上Bug_ 太难了。。。
    让API并行调用变得如丝般顺滑的绝招
  • 原文地址:https://www.cnblogs.com/martian148/p/14062586.html
Copyright © 2011-2022 走看看