【IOI2009】bzoj3352 旅行商

175 篇文章 0 订阅
148 篇文章 0 订阅

先考虑按时间顺序转移,可以写出状态 O(n) ,转移 O(n) 的方程。线段树优化可以做到 O(logn) 转移。
接下来考虑相同时间之间的转移。按照位置的顺序正反扫一遍就可以了。

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=4000010,oo=0x3f3f3f3f;
int rd()
{
    int x=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return x;
}
struct str
{
    int t,p,w;
    bool operator < (const str &s) const
    {
        if (t!=s.t) return t<s.t;
        return p<s.p;
    }
}a[maxn];
int dp[maxn],f[maxn],g[maxn],mx[2][maxn],n,m,U,D,S;
void build(int u,int L,int R)
{
    if (L==R)
    {
        if (L==S) mx[0][u]=D*S,mx[1][u]=-U*S;
        else mx[0][u]=mx[1][u]=-oo;
        return;
    }
    int mid=(L+R)/2;
    build(u*2,L,mid);
    build(u*2+1,mid+1,R);
    mx[0][u]=max(mx[0][u*2],mx[0][u*2+1]);
    mx[1][u]=max(mx[1][u*2],mx[1][u*2+1]);
}
int qry(int f,int u,int L,int R,int l,int r)
{
    if (l<=L&&R<=r) return mx[f][u];
    int mid=(L+R)/2,ret=-oo;
    if (l<=mid) ret=qry(f,u*2,L,mid,l,r);
    if (r>mid) ret=max(ret,qry(f,u*2+1,mid+1,R,l,r));
    return ret;
}
void modi(int u,int L,int R,int k)
{
    if (L==R)
    {
        mx[0][u]=dp[k]+D*a[k].p;
        mx[1][u]=dp[k]-U*a[k].p;
        return;
    }
    int mid=(L+R)/2;
    if (a[k].p<=mid) modi(u*2,L,mid,k);
    else modi(u*2+1,mid+1,R,k);
    mx[0][u]=max(mx[0][u*2],mx[0][u*2+1]);
    mx[1][u]=max(mx[1][u*2],mx[1][u*2+1]);
}
int main()
{
    //freopen("e.in","r",stdin);
    n=rd();
    U=rd();
    D=rd();
    S=rd();
    m=S;
    for (int i=1;i<=n;i++)
    {
        a[i].t=rd();
        a[i].p=rd();
        a[i].w=rd();
        m=max(m,a[i].p);
    }
    sort(a+1,a+n+1);
    n++;
    a[n]=(str){a[n-1].t+1,S,0};
    build(1,1,m);
    for (int i=1,j;i<=n;i=j+1)
    {
        for (j=i;j<n&&a[j+1].t==a[i].t;j++);
        for (int k=i;k<=j;k++)
            dp[k]=max(qry(0,1,1,m,1,a[k].p)-D*a[k].p,qry(1,1,1,m,a[k].p,m)+U*a[k].p)+a[k].w;
        f[i]=dp[i];
        for (int k=i+1;k<=j;k++)
            f[k]=max(dp[k],f[k-1]-D*(a[k].p-a[k-1].p)+a[k].w);
        g[j]=dp[j];
        for (int k=j-1;k>=i;k--)
            g[k]=max(dp[k],g[k+1]-U*(a[k+1].p-a[k].p)+a[k].w);
        for (int k=i;k<=j;k++)
        {
            dp[k]=max(dp[k],max(f[k],g[k]));
            modi(1,1,m,k);
        }
    }
    printf("%d\n",dp[n]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值