CF115E

liuboom發表於2024-12-06

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();
}