Linear Kingdom Races

题目链接

就拿此题来唤起我四年前的线段树优化残存的记忆吧


首先先考虑\(n^2\)算法:

\(f[i]\)表示\(1~i\)能获得的最大利润

\(f[i]=max{f[j]-cost(j+1,i)+val(j+1,i)}\),其中\(cost(i,j)\)表示修好\([i,j]\)需要的花费,\(val(i,j)\)表示修好\([i,j]\)的所有路能获得的收益

其中\(1<=j<i\)


\(n\)方算法时间主要花费在枚举\(j\)点,找出最大值,考虑维护\(c[j]=f[j]-cost(j+1,i)+val(j+1,i)\)其中\(i\)为当前枚举到的位置

由于每到一个新的\(i\),需要更新前面某些区间的所有\(c[j]\),以及转移是找到\(c[j]\)的最大值,因此考虑用线段树维护,每次转移以及修改时间复杂度降低为\(logn\)


具体细节:

\(cost(j+1,i)\)的更新:每次到一个新的点,之前的每个\(c[j]\)里面的\(cost\)都是\((j+1,i-1)\),因此\(c[j]\)需要减去\(a[i]\),\(a[i]\)为修当前的路的花费

\(val(j+1,i)\)的更新:每次到一个新的点,如果该点是某个区间的右端点,记这个区间为\((l,i)\),那么对于\(c[1~l-1]\),\(val(j+1,i)\)就应该加上这个区间的收益

某些小细节在代码里体现了,可以仔细琢磨

点击查看代码
#include<bits/stdc++.h>
#include<vector>
#define int long long
#define inf 1e18
#define inc 0xcfcfcfcf
#define N 200007
#define M 500007
#define mod 1000000007
//#pragma GCC optimize(2)
//#pragma GCC optimize(3)
using namespace std;
struct Tree
{
	int l,r,mx;
	int tag_add;
}tr[N<<2];
int T=1,n,m,x,y;
int a[N];
vector<pair<int,int>> g[N];
inline int Read()
{
	char ch=getchar();bool f=0;int x=0;
	for(;!isdigit(ch);ch=getchar())if(ch=='-')f=1;
	for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	if(f==1)x=-x;return x;
}
void Pushup(int now)
{
	tr[now].mx=max(tr[now<<1].mx,tr[now<<1|1].mx);
}
void Pushdown(int now)
{
	if(tr[now].tag_add)
	{
		tr[now<<1].tag_add+=tr[now].tag_add;
		tr[now<<1|1].tag_add+=tr[now].tag_add;
		tr[now<<1].mx+=tr[now].tag_add;
		tr[now<<1|1].mx+=tr[now].tag_add;
		tr[now].tag_add=0;
	 } 
}
void Add(int now,int l,int r,int ll,int rr,int val)
{
	if(ll<=l&&r<=rr)
	{
		tr[now].tag_add+=val;
		tr[now].mx+=val;
		return;
	}
	int mid=l+((r-l)>>1);
	Pushdown(now);
	if(ll<=mid)
		Add(now<<1,l,mid,ll,rr,val);
	if(rr>mid) 
		Add(now<<1|1,mid+1,r,ll,rr,val);
	Pushup(now);
}
bool Solve()
{
 	//freopen("test.in","r",stdin);
 	n=Read(),m=Read();
 	for(int i=1;i<=n;++i)
 		a[i]=Read();
	for(int i=1;i<=m;++i)
	{
	 	int in1,in2,in3;
	 	in1=Read(),in2=Read(),in3=Read(); 
	 	g[in2].push_back(make_pair(in1,in3));
	}
	int ans=0;
	for(int i=1;i<=n;++i)
	{
		Add(1,0,n,0,i-1,-a[i]);
		for(auto j=g[i].begin();j!=g[i].end();++j)
			Add(1,0,n,0,j->first-1,j->second);
		ans=max(ans,tr[1].mx);
		Add(1,0,n,i,i,ans);
		//printf("%I64d ",ans);
	}
	cout<<ans<<endl;
	return true;
}
signed main()
{
	//scanf("%lld",&T);
	while(T--)
		if(!Solve())
			printf("-1\n");
	return 0;
}
/*
-std=c++11
-std=c99
*/


posted @ 2023-03-23 22:24  模拟退火  阅读(16)  评论(0编辑  收藏  举报