洛谷 P5902 [IOI2009]salesman(dp)

题面传送门

题意:
有 \(n\) 个展销会,每个展销会给出它的时间 \(t_i\),举办展销会的位置 \(l_i\),和参加这个展销会你能得到的收益 \(m_i\)。
你现在在位置 \(s\),你可以花 \(u\) 的代价从 \(i\) 走到 \(i-1\),或是花 \(d\) 的代价从 \(i\) 走到 \(i+1\)。
你只能先参加时间靠前的展销会,再参加时间靠后的展销会。
问从 \(s\) 出发并回到 \(s\) 能够获得的最大收益。
\(1 \leq n,t_i \leq 5 \times 10^5\),\(1 \leq l_i \leq 5 \times 10^5+1\),可能存在相同的 \(t_i\)。

这是一道 dp 题。
先考虑一个弱化版本的问题,假设没有 \(t_i\) 相同。
不难想到将这 \(n\) 个展销会按 \(t_i\) 从小到大排个序。
然后设 \(dp_i\) 表示参加前 \(i\) 个展销会,当前在第 \(i\) 个展销会的最大收益。
枚举上一个参加的展销会,于是我们就有 \(dp_i=\max\{dp_j-cost(j,i)\}+m_i\),其中 \(cost(j,i)\) 表示从第 \(j\) 个展销会所在的位置到达第 \(i\) 个展销会所在的位置的代价。
我们进一步对 \(cost(j,i)\) 进行展开,就有
\(dp_i=\max\{dp_j-(l_j-l_i)\times u\}+m_i\quad(l_j\geq l_i)\)
\(dp_i=\max\{dp_j-(l_i-l_j)\times d\}+m_i\quad(l_j\lt l_i)\)
拿第一个柿子举例,将 \(dp\) 柿子拆成与 \(i\) 有关的和与 \(j\) 有关的两部分,也就有
\(dp_j-(l_j-l_i)\times u=dp_j-l_j\times u+l_i\times u=(dp_j-l_j\times u)+l_i\times u\)
用树状数组维护 \(dp_j-l_j\times u\) 的最大值就可以在 \(\mathcal O(\log n)\) 的时间内求出 \(dp_i\)


但加上 \(t_i\) 可能相同的条件怎么办呢?
在举办时间相同的展销会当中,我们肯定是从左往右依次经过某些展销会,或是从右到左依次经过某些展销会,不走回头路。
于是我们可以把这 \(t_i\) 相同的展销会按 \(l_i\) 从小到大排个序。(显然这 \(t_i\) 相同的展销会的下标能够组成一个区间)
先求出从举办时间小于 \(t_i\) 的展销会到达 \(i\) 的最小代价 \(x_i\)。
然后设 \(dpl_i\) 表示以 \(i\) 结尾的一条从左至右的路径的最小代价。
\(dpr_i\) 表示以 \(i\) 结尾的一条从右至左的路径的最小代价。
以 \(dpl_i\) 为例,要么从从举办时间小于 \(t_i\) 的展销会到达展销会 \(i\),也就是 \(x_i\),要么从展销会 \(i-1\) 到底 \(i\),也就是 \(dp_{i-1}-(l_i-l_{i-1})\times d+m_i\),两者取一个 \(\max\) 即可。
最后 \(dp_i=\max(dpl_i,dpr_i)\)。
别忘了最后要回到位置 \(s\)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=500000+5;
const int MAXD=500001+5;
void chkmin(ll &x,ll y){(x>y)?(x=y):0;}
void chkmax(ll &x,ll y){(x<y)?(x=y):0;}
int n,s;ll u,d;
struct fair{
	int t;ll l,m;
	friend bool operator <(fair x,fair y){
		if(x.t!=y.t) return x.t<y.t;
		return x.l<y.l;
	}
} a[MAXN];
struct fenwick_tree{
	ll t[MAXD];
	fenwick_tree(){memset(t,128,sizeof(t));}
	void add(int x,ll v){for(int i=x;i<MAXD;i+=(i&(-i))) chkmax(t[i],v);}
	ll query(int x){ll ret=-1e18;for(int i=x;i;i-=(i&(-i))) chkmax(ret,t[i]);return ret;}
} t1,t2;
ll dp[MAXN],dpd[MAXN],dpu[MAXN];
int main(){
	scanf("%d%lld%lld%d",&n,&u,&d,&s);
	t1.add(s,s*d);t2.add(MAXD-s+1,-s*u);
	for(int i=1;i<=n;i++) scanf("%d%lld%lld",&a[i].t,&a[i].l,&a[i].m);
	sort(a+1,a+n+1);memset(dp,128,sizeof(dp));
	int l=1,r=1;
	while(l<=n){
		r=l;while(r<=n&&a[r].t==a[l].t) r++;r--;
//		printf("%d %d\n",l,r);
		for(int i=l;i<=r;i++){
			dp[i]=max(dp[i],t1.query(a[i].l)-a[i].l*d);
			dp[i]=max(dp[i],t2.query(MAXD-a[i].l-1)+a[i].l*u);
			dp[i]+=a[i].m;
			dpd[i]=dpu[i]=dp[i];
		}
		for(int i=l+1;i<=r;i++) dpd[i]=max(dpd[i],dpd[i-1]-(a[i].l-a[i-1].l)*d+a[i].m);
		for(int i=r-1;i>=l;i--) dpu[i]=max(dpu[i],dpu[i+1]-(a[i+1].l-a[i].l)*u+a[i].m);
		for(int i=l;i<=r;i++) dp[i]=max(dpd[i],dpu[i]);
		for(int i=l;i<=r;i++) t1.add(a[i].l,dp[i]+a[i].l*d);
		for(int i=l;i<=r;i++) t2.add(MAXD-a[i].l,dp[i]-a[i].l*u);
		l=r+1;
	}
//	for(int i=1;i<=n;i++) printf("%lld\n",dp[i]);
	ll ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,dp[i]-((a[i].l>s)?(a[i].l-s)*u:(s-a[i].l)*d));
	printf("%lld\n",ans);
	return 0;
}
/*
dp[i]=max{dp[j]+l[j]*d-l[i]*d}(l[j]<=l[i])
dp[i]=max{dp[j]-l[j]*u+l[i]*u}(l[j]>l[i])
*/
上一篇:Oracle 数据采样


下一篇:在MySQL中创建dept,emp,salgrade三张表,以供学习使用