CF115E

CF115E

Linear Kingdom Races

题面翻译

题目描述

你是一个赛车比赛的组织者,想在线性王国中安排一些比赛。

线性王国有 \(n\) 条连续的从左到右的道路。道路从左到右依次编号为从 \(1\)\(n\),因此道路按照升序排列。在这些道路上可能会有几场比赛,每一场比赛都将使用这些道路的某个连续的子序列。而且,如果某场比赛举行了,你将获得一定数额的金钱。没有比赛在时间上重叠,所以每一段道路可以在多个比赛中使用。

不幸的是,所有道路的状况都不佳,需要修理。每条路都有与之相关的维修费用,你需要支付这笔费用来修理道路。只有在某场比赛中需要使用的所有道路都进行了修复,才能进行比赛。你的任务是修复道路并使你的利润最大化。你的利润被定义为你从比赛中获得的总金额减去你花在修理道路上的钱。请注意,您可以决定不修任何道路,并获得利润 \(0\)

输出你能获得的最大利润。

线段树优化dp模板题

首先我们把这个问题想成染色:
\(dp[i][0/1]\) 表示第i个点染或不染时的收获

则有:
\( dp[i][0]=max(dp[i-1][0],dp[i-1][1]) \)

\( dp[i][1]=Max ({max(dp[j][1],dp[j][0])-(sum[i]-sum[j])} )|j<i \)

对于dp[0]的转移是简单的,但是对于dp[1],如果我们使用最朴素的转移,我们就会得到\(o(n^2)\) 的时间复杂度

看着这个式子,我们不难想到线段树优化:

首先开一颗线段树维护dp[1]的转移值,初值为\(-inf\)
然后对于每条道路\(i\),打一个\(-a[i]\)\(tag\)\([1,i-1]\)

我们来解释一下这样做的意义是什么:
对于当前节点r,如果线段树取了一个点l,那么就表示当前最后一个染色区间为\([l,r]\)
然后又因为当前在节点r,也就是说节点r之前的所有节点都已经加入线段树了那么我们在取一个\(t[x].v\)时,取到的值就应该是 \(dp[j]-(sum[i]-sum[j])\)

这样写就符合了我们之前想要的状态转移方程

然后再考虑每个任务:

既然当前节点r与线段树上最优节点l组成了一个区间[l,r],表示最后一个涂色区间为[l,r]
那么对于任务的表示就应该在每一次到达任务右节点R时,将其左节点L在[1,L]打上标记:

也就是表示当前染色区间是[l,r],已经满足R<=r,
接下来只需要满足l<=L就能获得这个区间的贡献

然后这题就做完了

但是还要扣一些细节(详见代码)

#include<bits/stdc++.h>
#define ls x<<1
#define rs x<<1|1
#define int long long 
const int N=2e5+5;
const int inf =1e17;
using namespace std;
int n,m;
int dp[N][2],a[N];
void init()
{
	for(int i=0;i<N;i++)
	{
		dp[i][0]=dp[i][1]=-inf;
	}
}
struct Task{
	int l,r,val;
}q[N];
struct Tree{
	int l,r,tag,val;
}t[N<<2];
void pushup(int x)
{
	t[x].val=max(t[ls].val,t[rs].val);
	return ;
}
void pushdown(int x)
{
	int tag=t[x].tag;
	if(tag)
	{
		t[ls].tag+=tag,t[rs].tag+=tag;
		t[ls].val+=tag,t[rs].val+=tag;
	}
	t[x].tag=0;
	return ;
}
void build(int x,int l,int r)
{
	t[x].l=l,t[x].r=r;
	if(l==r)
	{
		t[x].val=-inf;
		return ;
	}
	int mid=l+r>>1;
	build(ls,l,mid);build(rs,mid+1,r);
	pushup(x);
}
void upd(int x,int ll,int rr,int k)
{
	//cout<<t[x].l<<" "<<t[x].r<<"=="<<ll<<" "<<rr<<"\n";
	if(ll>rr)return ;
	if(ll<=t[x].l&&t[x].r<=rr)
	{
		t[x].tag+=k;t[x].val+=k;
		return ;
	}
	pushdown(x);
	int mid=t[x].l+t[x].r>>1;
	if(ll<=mid)upd(ls,ll,rr,k);
	if(mid<rr)upd(rs,ll,rr,k);
	pushup(x);
}
void query(int x,int ll,int rr,int &res)
{
	if(ll<=t[x].l&&t[x].r<=rr)
	{
		res=max(res,t[x].val);
		return ;
	}
	pushdown(x);
	int mid=t[x].l+t[x].r>>1;
	if(ll<=mid)query(ls,ll,rr,res);
	if(mid<rr)query(rs,ll,rr,res);
	pushup(x);
}
bool cmp(Task t1,Task t2)
{
	return t1.r<t2.r;
}
void work()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&q[i].l,&q[i].r,&q[i].val);
	}
	sort(q+1,q+1+m,cmp);
	int now=1;
	build(1,1,n+1);
	upd(1,1,1,inf);
	//cout<<"upd1";
	for(int i=1;i<=n;i++)
	{
		upd(1,1,i-1,-a[i-1]);
		while(q[now].r==i)
		{
			upd(1,1,q[now].l,q[now].val);
			now++;
		}
		dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
		query(1,1,i,dp[i][1]);
		dp[i][1]-=a[i];
		int v=inf+max(dp[i][0],dp[i][1]);
		upd(1,i+1,i+1,v);
	}
	int ans=max(dp[n][0],dp[n][1]);
	printf("%lld\n",ans);
}
#undef ls 
#undef rs
#undef int 
int main()
{
//	freopen("CF115E.in","r",stdin);
	work();
}
posted @ 2024-12-06 11:51  liuboom  阅读(18)  评论(0)    收藏  举报