zoj 3632 Watermelon Full of Water(DP+线段树)

题意:有个人每天都要吃西瓜。。。每天的西瓜的价钱不同,买了西瓜后,最多可以吃ti天,每天最多买一个西瓜,如果一天买了一个西瓜,那么之前买的就要丢掉。问n天花掉的最少多少钱能保证这个人每天都能吃到西瓜。

思路:dp方程还是挺好想的。dp[i][j]表示在第i天是否买西瓜所花费的最小价值,dp[i][1]=min(dp[i-1][0],dp[i-1][1])+val[i]。dp[i][0]=min(dp[j][1]) (其中j+days[j]>=i)。前面的方程好算,后面的部分用线段树维护最小值即可。

代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<set>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=500000+10;
ll minv[maxn<<2],setv[maxn<<2];
ll dp[maxn][2],val[maxn],days[maxn];
void PushUp(int rt)
{
    minv[rt]=min(minv[rt<<1],minv[rt<<1|1]);
}
void PushDown(int rt)
{
    if(setv[rt]!=-1)
    {
        if(setv[rt<<1]!=-1) setv[rt<<1]=min(setv[rt<<1],setv[rt]);
        else setv[rt<<1]=setv[rt];
        if(setv[rt<<1|1]!=-1) setv[rt<<1|1]=min(setv[rt<<1|1],setv[rt]);
        else setv[rt<<1|1]=setv[rt];
        minv[rt<<1]=min(minv[rt<<1],setv[rt]);
        minv[rt<<1|1]=min(minv[rt<<1|1],setv[rt]);
        setv[rt]=-1;
    }
}
void build(int l,int r,int rt)
{
    minv[rt]=Inf;setv[rt]=-1;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
ll Query(int L,int R,int l,int r,int rt)
{
    if(l>=L&&r<=R)
    {
        return minv[rt];
    }
    int m=(l+r)>>1;
    PushDown(rt);
    ll ans=Inf;
    if(m>=L) ans=min(ans,Query(L,R,l,m,rt<<1));
    if(m<R) ans=min(ans,Query(L,R,m+1,r,rt<<1|1));
    return ans;
}
void Update(int L,int R,int l,int r,int rt,ll v)
{
    if(l>=L&&r<=R)
    {
        if(setv[rt]!=-1) setv[rt]=min(setv[rt],v);
        else setv[rt]=v;
        minv[rt]=min(minv[rt],v);
        return ;
    }
    PushDown(rt);
    int m=(l+r)>>1;
    if(m>=L) Update(L,R,l,m,rt<<1,v);
    if(m<R) Update(L,R,m+1,r,rt<<1|1,v);
    PushUp(rt);
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(~scanf("%d",&n))
    {
        build(1,n,1);
        for(int i=1;i<=n;++i)
            scanf("%lld",&val[i]);
        for(int i=1;i<=n;++i)
            scanf("%lld",&days[i]);
        dp[1][0]=Inf;dp[1][1]=val[1];
        Update(1,min((ll)n,days[1]),1,n,1,val[1]);
        for(int i=2;i<=n;++i)
        {
            dp[i][1]=min(dp[i-1][0],dp[i-1][1])+val[i];
            dp[i][0]=Query(i,i,1,n,1);
            Update(i,min((ll)n,i+days[i]-1),1,n,1,dp[i][1]);
        }
        printf("%lld\n",min(dp[n][0],dp[n][1]));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值