题解 [ABC353G] Merchant Takahashi

【洛谷专栏】

没打这次比赛,赛后来看题发现相较于其他场的 G 较简单。

题意

\(N\) 个小镇和 \(M\) 次市场,从小镇 \(i\) 移动到小镇 \(j\) 需花费 \(C \times \vert i-j \vert\) 元。

每次交易依次举行,第 \(i\) 次交易在小镇 \(T_i\) 举行,可获得 \(P_i\) 元。

你需要从小镇 \(1\) 出发,选择参加一些交易活动,来获取最多的钱。过程中无需保证钱非负。

分析

贪心无从下手,考虑动态规划。

状态设计

\(f_i\) 表示结束点在小镇 \(i\) 可以获取到的最多钱数。

初始状态

\[f_i = \begin{cases}0 & i=1 \\ -\infty & \text{otherwise}\end{cases} \]

转移方程

设某次交易在小镇 \(T\) 举行,可获得 \(P\) 元。

\[f_T = \max\limits^{1 \le i \le n \land i \neq T}{(f_i + C \times \vert T - i\vert)} + P \]

可以用线段树优化,将绝对值拆开。

对于 \(i < T\) 的点维护 \(f_i - C \times i\) 的最大值,对于 \(i > T\) 的点维护 \(f_i + C \times i\) 的最大值,然后分别处理即可。

代码

//the code is from chenjh
#include<cstdio>
#include<algorithm>
#define MAXN 200002
using namespace std;
typedef long long LL;
const LL infll=0x3f3f3f3f3f3f3f3fll;
using ci=const int;
int n,c,m;
struct SEG{//线段树。
	#define lson (rt<<1)
	#define rson (rt<<1|1)
	LL mx[MAXN<<2];
	void build(ci rt,ci l,ci r){
		if(l==r){mx[rt]=-infll;return;}//初始化为极小值。
		int mid=(l+r)>>1;
		build(lson,l,mid),build(rson,mid+1,r);
		mx[rt]=max(mx[lson],mx[rson]);
	}
	void update(ci rt,ci l,ci r,ci pos,const LL&val){
		if(l==r){mx[rt]=max(mx[rt],val);return;}//对于每次交易取最大值。
		int mid=(l+r)>>1;
		pos<=mid?update(lson,l,mid,pos,val):update(rson,mid+1,r,pos,val);
		mx[rt]=max(mx[lson],mx[rson]);
	}
	LL query(ci rt,ci l,ci r,ci L,ci R){
		if(L<=l && r<=R) return mx[rt];
		int mid=(l+r)>>1;
		LL ret=-infll;
		if(L<=mid) ret=max(ret,query(lson,l,mid,L,R));
		if(mid<R) ret=max(ret,query(rson,mid+1,r,L,R));
		return ret;
	}
};
SEG s1,s2;
int main(){
	scanf("%d%d%d",&n,&c,&m);
	s1.build(1,1,n),s2.build(1,1,n);
	s1.update(1,1,n,1,c),s2.update(1,1,n,1,-c);//s1 维护绝对值加的部分,s2 维护绝对值减的部分。
	int t;LL p;
	for(int i=1;i<=m;i++){
		scanf("%d%lld",&t,&p);
		LL s=max(s1.query(1,1,n,1,t)-(LL)c*t,s2.query(1,1,n,t,n)+(LL)c*t)+p;//左边和右边分别取最大值。
		s1.update(1,1,n,t,s+(LL)c*t),s2.update(1,1,n,t,s-(LL)c*t);//更新当前点最大值。
	}
	LL ans=0;
	for(int i=1;i<=n;i++) ans=max({ans,s1.query(1,1,n,i,i)-(LL)c*i,s2.query(1,1,n,i,i)+(LL)c*i});//把扣减的距离加回来,取答案最大值。
	printf("%lld\n",ans);
	return 0;
}
posted @ 2025-02-09 19:53  Chen_Jinhui  阅读(12)  评论(0)    收藏  举报