Description
有 n n 条道路从左至右编号为~ n n ,道路均损坏,需要修补才能使用,第条道路修补的费用为 cost[i] c o s t [ i ] ,现在有 m m 场比赛要举行,其中第场比赛需要使用 [li,ri] [ l i , r i ] 区间的道路,如果可以成功举办第 i i 场比赛则获利为,且由于每场比赛时间不同故不需要考虑两场比赛场地冲突,问最大获利
Input
第一行两个整数 n,m n , m 分别表示道路数量和比赛场数,之后输入 n n 个整数表示修补第 i i 条道路的花费,最后行每行输入三个整数 li,ri,pi l i , r i , p i 表示第 i i 场比赛的场地区间和利润
Output
输出最大获利
Sample Input
7 4
3
2
3
2
1
2
3
1 2 5
2 3 5
3 5 3
7 7 5
Sample Output
4
Solution
以 dp[i] d p [ i ] 表示只考虑前 i i 条道路的最大获利,假设要从第条道路一直修到第 i i 条道路,花费为,利润较之 dp[j] d p [ j ] 多了所有区间右端点不大于 i i 而左端点大于的比赛利润,假设该段利润为 pay(j+1,i) p a y ( j + 1 , i ) ,那么有转移
dp[i]=max(dp[j]+pay(j+1,i)−cost(j+1,i),0≤j<i) d p [ i ] = m a x ( d p [ j ] + p a y ( j + 1 , i ) − c o s t ( j + 1 , i ) , 0 ≤ j < i )
直接求 pay(j+1,i) p a y ( j + 1 , i ) 比较麻烦,换个角度,场地在 [li,ri] [ l i , r i ] 的比赛会对所有 dp[j]→dp[i] d p [ j ] → d p [ i ] 的转移产生影响,只要 j<li≤ri≤i j < l i ≤ r i ≤ i ,那么把所有比赛按右端点排序,用线段树维护 dp[j]+pay(j+1,i)−cost(j+1,i) d p [ j ] + p a y ( j + 1 , i ) − c o s t ( j + 1 , i ) 的最大值,对于当前考虑的 i i ,加入所有右端点不超过 i i 的比赛,假设其左端点为 l l ,那么该场比赛会增加所有 j<l j < l 的 pay(j+1,i) p a y ( j + 1 , i ) ,也即对区间 [0,l−1] [ 0 , l − 1 ] 加上其利润 p p ,而对于 cost(j+1,i) c o s t ( j + 1 , i ) 的维护,每次考虑 i i 之前把 −cost[i] − c o s t [ i ] 加到区间 [0,i−1] [ 0 , i − 1 ] 即可, dp[n] d p [ n ] 即为答案,时间复杂度 O((m+n)logn+mlogm) O ( ( m + n ) l o g n + m l o g m )
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=200005;
struct node
{
int l,r,p;
bool operator<(const node &b)const
{
return r<b.r;
}
}a[maxn];
int n,m,cost[maxn];
#define ls (t<<1)
#define rs ((t<<1)|1)
ll dp[maxn],Max[maxn<<2],Lazy[maxn<<2];
void push_up(int t)
{
Max[t]=max(Max[ls],Max[rs]);
}
void push_down(int t)
{
if(Lazy[t])
{
Lazy[ls]+=Lazy[t];
Lazy[rs]+=Lazy[t];
Max[ls]+=Lazy[t];
Max[rs]+=Lazy[t];
Lazy[t]=0;
}
}
void update(int L,int R,int l,int r,int t,ll v)
{
if(L<=l&&r<=R)
{
Lazy[t]+=v;
Max[t]+=v;
return ;
}
push_down(t);
int mid=(l+r)/2;
if(L<=mid)update(L,R,l,mid,ls,v);
if(R>mid)update(L,R,mid+1,r,rs,v);
push_up(t);
}
ll query(int L,int R,int l,int r,int t)
{
if(L<=l&&r<=R)return Max[t];
push_down(t);
int mid=(l+r)/2;
ll ans=0;
if(L<=mid)ans=max(ans,query(L,R,l,mid,ls));
if(R>mid)ans=max(ans,query(L,R,mid+1,r,rs));
push_up(t);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&cost[i]);
for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].p);
sort(a+1,a+m+1);
for(int i=1,j=1;i<=n;i++)
{
update(0,i-1,0,n,1,-cost[i]);
while(j<=m&&a[j].r<=i)
{
update(0,a[j].l-1,0,n,1,a[j].p);
j++;
}
dp[i]=max(dp[i-1],query(0,i-1,0,n,1));
update(i,i,0,n,1,dp[i]);
}
printf("%I64d\n",dp[n]);
return 0;
}