JZOJ5347【NOIP2017提高A组模拟9.5】遥远的金字塔 斜率优化 DP

92 篇文章 1 订阅
4 篇文章 0 订阅

题意:给你一个金字塔(每层长度不严格递减),问你用k个矩形覆盖,最多的面积。
n<=2e4,m<=100.

首先n^2k的dp随便搞搞就可以了。
然后问题是怎么优化。
我们设f[i]选了p个,g[i]选了p-1个矩形的最多面积。
那么明显有:
f[i]=max(g[j]+len[i](ij))
设有决策点j,k (k<j) ,假设j比k优,那么可以列式:
g[j]+len[i](ij)>g[k]+len[i](ik)
进而有: g[j]+len[i]ilen[i]j>g[k]+len[i]ilen[i]k
g[j]g[k]>len[i](jk)
(g[j]g[k])/(jk)>len[i]
S(i,j) (g[i]g[j])/(ij)
那么我们可以用这个结果直接单调队列扫一遍就好了。具体式子写在code中。
答案从所有的里面取个max。
这题可能是我的第一个斜率优化。。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=2e5+5;
typedef long long ll;
ll f[N],g[N],ans,len[N],q[N];
int n,m,t,w;
int main()
{
    freopen("pyramid.in","r",stdin);
    freopen("pyramid.out","w",stdout); 
    scanf("%d%d",&n,&m);m--;
    fo(i,1,n)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        len[i]=y-x+1;
    }
    fo(i,1,n)g[i]=len[i]*i;
    memcpy(f,g,sizeof(f));
    while (m--)
    {
        memset(f,0,sizeof(f));
        t=0,w=0;q[0]=0;
        fo(i,1,n)
        {//G[j]-G[k]/(j-k)
            //S(q[L+1],q[L])>w[i]
            while (t<w&&g[q[t+1]]-g[q[t]]>len[i]*(q[t+1]-q[t]))t++; 
            f[i]=g[q[t]]+len[i]*(i-q[t]);
            //S(q[R],q[R-1])<S(i,q[R])
            while (t<w&& (g[q[w]]-g[q[w-1]])*(i-q[w]) < (q[w]-q[w-1])*(g[i]-g[q[w]]) )w--;
            q[++w]=i;
        }
        memcpy(g,f,sizeof(g));
    }
    fo(i,1,n)ans=max(ans,f[i]);
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值