Bzoj3352 [ioi2009]旅行商

时间:2023-12-11 11:23:08

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 89  Solved: 36

Description

旅行商认定如何优化旅行路线是一个非常棘手的计算问题,所以他决定沿着线性的多瑙河开展他的业务。他有一条快船能够在瞬间沿着多瑙河把他从任意开始的位置带到任意目的地,但是这条船很费油。它逆流而上(驶向源头的方向)每米花费U元,顺流而下每米花费D元(驶离源头的方向)。
沿着多瑙河有N个展销会是旅行商所感兴趣的。每个展销会只持续一天。对于任意一个展销会X,旅行商知道:①展销日期是Tx(该日期是从旅行商买船之日算起过去的天数),②展销地点是Lx(该地点用它与源头的距离表示,单位为米),③参加该展销会能赚到的钱数是Mx元。旅行商开始和结束旅程的地点都是他位于多瑙河边的家S (用它与源头的距离表示,单位为米)。
请帮助旅行商选择他是否参加,如果参加应该以什么样的顺序参加哪些展销会才能在旅行结束时获得最多的总收益。旅行商的总收益定义为他参加的所有展销会的收益和,减去他在河上顺流和逆流航行的总花费。
注意:如果展销会A在展销会B之前举行并且旅行商要参加这两个展销会,旅行商必须先参加A,之后才能参加B(即他不能先参加B后参加A)。当两个展销会在同一天举行,他可以按任意顺序参加这两个展销会。旅行商在一天之内参加的展销会的数目没有限制,但是他不能参加同一个展销会两次并在一个展销会上获得两次收益。他可以经过他已经参加过的展销会而不再获得任何收益。
任务
写一个程序,给定所有展销会的日期、地点和收益,以及旅行商的家的位置和他旅行的花费,计算他在旅行结束时可能获得的最大的总收益。
数据规模
1≤N≤500,000     展销会的数目
1≤D≤U≤10          旅行每米的花费,逆流而上(U)和顺流而下(D)
1≤S≤500,001     旅行商的家的位置
1≤Tk≤500,000       展销会k举行的日期
1≤Lk≤500,001       展销会k的地点
1≤Mk≤4,000      旅行商参加展销会k所能获得的收益

Input

你的程序需要从标准输入读人下列数据:
· 第一行依次包含整数N,U,D和S,整数之间用空格隔开。
· 接下来的N行描述了N个展销会的情况(不按特定顺序)。这N行中的第kth行描述了第kth个展销会的情况,它包含三个整数(整数之间分别用一个空格隔开)分别表示展销会的日期Tk,地点Lk,和收益Mk。
注意:输人中给出的所有地点都是不同的。即没有两个展销会在同一个地点举行,也没有展销会在旅行商的家的位置上举行。

Output

你的程序必须向标准输出写入一行,该行包含一个整数:旅行商结束旅行时能够获得的最大总收益。

Sample Input

4 5 3 100
2 80 100
20 125 130
10 75 150
5 120 110

Sample Output

50

HINT

Source

动态规划 线性dp 线段树优化

设 $f[i][j]$ 表示第 i 天停在j号展会位置时的最大收益。
显然有 $ f[i][j] = max{f[i-1][k] * cost(k,j)}+ w[j] $
注意到天数和展会数同级,可以认为多数展会不在同一天,这时可以把第一维i丢掉。
丢掉以后空间够了,时间复杂度还是太高啊?
我们把cost展开,当顺流而下的时候,假设向i转移的时候,j位置比k位置优,有:
$$ f[j] - (L_i-L_j) * D > f[k] - (L_i-L_k) * D $$
$$ f[j]+L_j*D > f[k] + L_k*D $$
把$ f[j]+L_j*D $ 丢进线段树里,维护区间最大值即可$O(logn)$查询。
逆流而上同理。

当一些展会挤在同一天的时候怎么处理?
撕烤一下可以发现同一天经过的展会一定是一个连续的区间(从一点到另一点的时候如果中途经过别的展会,不拿白不拿)。
对于每一个终点,分类讨论起点在上游还是下游,枚举起点找最大收益,这样显然是正确的。
维护two-pointers或者前缀最大值即可快速转移。

78行的初始化边界没有加,WA*2

 #include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int INF=0x3f3f3f3f;
const int mxn=;
int read(){
int x=,f=;char ch=getchar();
while(ch<'' || ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>='' && ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int n,m,D,U,S;
struct met{
int day,x;
int w;
bool operator < (const met &b)const{
return (day==b.day && x<b.x) || day<b.day;
}
}a[mxn];
int f[mxn];
int tmp[mxn],tmp2[mxn];
//
struct node{
int mxu,mxd;
}t[mxn<<];
#define ls rt<<1
#define rs rt<<1|1
inline void pushup(int rt){
t[rt].mxd=max(t[ls].mxd,t[rs].mxd);
t[rt].mxu=max(t[ls].mxu,t[rs].mxu);
return;
}
void Build(int l,int r,int rt){
if(l==r){t[rt].mxd=t[rt].mxu=-INF;return;}
int mid=(l+r)>>;
Build(l,mid,ls);Build(mid+,r,rs);
pushup(rt);
return;
}
void update(int p,int l,int r,int rt){
if(l==r){
t[rt].mxd=f[p]+a[p].x*D; t[rt].mxu=f[p]-a[p].x*U;
return;
}
int mid=(l+r)>>;
if(a[p].x<=mid)update(p,l,mid,ls);
else update(p,mid+,r,rs);
pushup(rt);
return;
}
//
int MXU,MXD;
void query(int L,int R,int l,int r,int rt){
if(L<=l && r<=R){
MXD=max(t[rt].mxd,MXD);MXU=max(t[rt].mxu,MXU);
return;
}
int mid=(l+r)>>;
if(L<=mid)query(L,R,l,mid,ls);
if(R>mid)query(L,R,mid+,r,rs);
return;
}
void solve(){
Build(,m,);
update(,,m,);
for(int i=,j;i<=n;i=j+){
for(j=i;a[j].day==a[i].day;j++){
f[j]=-INF;
MXU=-INF; query(a[j].x,m,,m,);
f[j]=max(f[j],MXU+a[j].x*U);
MXD=-INF; query(,a[j].x,,m,);
f[j]=max(f[j],MXD-a[j].x*D);
f[j]+=a[j].w;
}j--;
tmp[i]=f[i];tmp2[j]=f[j];
for(int k=i+;k<=j;k++)
tmp[k]=max(f[k],tmp[k-]-(a[k].x-a[k-].x)*D+a[k].w);
for(int k=j-;k>=i;k--)
tmp2[k]=max(f[k],tmp2[k+]-(a[k+].x-a[k].x)*U+a[k].w);
for(int k=i;k<=j;k++){
f[k]=max(f[k],max(tmp[k],tmp2[k]));
update(k,,m,);
}
}
printf("%d\n",f[n]);
return;
}
int main(){
int i,j;
n=read();
U=read();D=read();
S=read();
a[]=(met){,S,};n++;
for(i=;i<=n;i++){
a[i].day=read();a[i].x=read();a[i].w=read();
m=max(m,a[i].x);
}
sort(a+,a+n+);++n;
a[n]=(met){a[n-].day+,S,};
m=max(m,S);
solve();
return ;
}