题目链接:小翔和泰拉瑞亚
题意
给你一个长度为n的序列和m个操作,每个操作中,你要将 l i ~ r i {l_i~r_i} li~ri区间内每个数减少 w i {w_i} wi。你可以从这m个操作中选择一部分,使得该序列(最大数-最小数)最大。
题解
本题一开始是毫无头绪的,如何选成了难题?
但是有一个思路却让我柳暗花明,就是把第i个数当作最小值,那么很容易我们可以想到把所有能减小第i个数的区间都算进去一定能得到最优子结构的解。
如果减小第i个数的区间不包含最大值,那么答案更优;如果包含,最大最小同时减对答案没有影响。
- 如果减完后第i个数不是最小值,那么当我们遍历到真正最小值时,能够更新答案得到结果。
- 如果减完后第i个数是最小值,那么我们能够得到第i个数作为最小值的结果,真正的答案一定是以某个数作为最小值的结果,所以我们遍历所有作为最小值的第i个数,就能得到答案。
在此相信很多人已经想到dp,dp[i]:以第i个数作为最小值的答案。那么如何将dp[i] ——> dp[i+1]转移。
其实也不难,遍历时遇到第i个为最小值,需要加上所有以i为左端点的操作区间,在计算完后,我们再删除以i为右端点的操作区间即可。
如果i在区间内,那么在前面就已经加过了,如果i刚好为右端点,那么后面第i+1个数与[x,i]无交集,所以删去即可。
所以我们维护一个有最大值和最小值的线段树,进行区间增加、删除操作即可。
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define lowbit(x) x&-x
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=2e9+10;
const int maxn=2e5+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
ll h[maxn];
ll addv[maxn<<2];
struct Tree
{
ll maxx,minn;
void init(ll maxx,ll minn)
{
this->maxx = maxx;
this->minn = minn;
}
}tree[maxn<<2];
struct Node
{
int l,r,w;
};
vector<Node> L[maxn],R[maxn];
void pushup(int p)
{
tree[p].maxx=max(tree[p*2].maxx,tree[p*2+1].maxx);
tree[p].minn=min(tree[p*2].minn,tree[p*2+1].minn);
}
void build(int p,int l,int r)
{
addv[p]=0;
if(l==r) {
tree[p].maxx=h[l]; tree[p].minn=h[l]; return ; }
int mid=l+(r-l)/2;
build(p*2, l, mid);
build(p*2+1, mid+1, r);
pushup(p);
}
void pushdown(int p)
{
if(addv[p])
{
addv[p*2]+=addv[p];
addv[p*2+1]+=addv[p];
tree[p*2].maxx+=addv[p];
tree[p*2].minn+=addv[p];
tree[p*2+1].maxx+=addv[p];
tree[p*2+1].minn+=addv[p];
addv[p]=0;
}
}
void add(int p,int l,int r,int addl,int addr,int v)
{
if(addl<=l && addr>=r)
{
addv[p]+=v;
tree[p].maxx+=v;
tree[p].minn+=v;
return ;
}
pushdown(p);
int mid=l+(r-l)/2;
if(addl<=mid) add(p*2, l, mid, addl, addr, v);
if(addr>mid) add(p*2+1, mid+1, r, addl, addr, v);
pushup(p);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&h[i]);
build(1,1,n);
while (m--) {
Node tmp;
scanf("%d%d%d",&tmp.l,&tmp.r,&tmp.w);
L[tmp.l].pb(tmp);
R[tmp.r].pb(tmp);
}
ll ans=-inf;
for(int i=1;i<=n;i++)
{
int lsize=L[i].size();
int rsize=R[i].size();
for(int j=0;j<lsize;j++) add(1, 1, n, L[i][j].l, L[i][j].r, -L[i][j].w);
ans=max(ans,tree[1].maxx-tree[1].minn);
for(int j=0;j<rsize;j++) add(1, 1, n, R[i][j].l, R[i][j].r, R[i][j].w);
}
printf("%lld\n",ans);
}