[2020牛客算法竞赛入门课第九节习题] 小翔和泰拉瑞亚 线段树+dp优化

题目链接:小翔和泰拉瑞亚

题意

给你一个长度为n的序列和m个操作,每个操作中,你要将 l i ~ r i {l_i~r_i} liri区间内每个数减少 w i {w_i} wi。你可以从这m个操作中选择一部分,使得该序列(最大数-最小数)最大。

题解

本题一开始是毫无头绪的,如何选成了难题?
但是有一个思路却让我柳暗花明,就是把第i个数当作最小值,那么很容易我们可以想到把所有能减小第i个数的区间都算进去一定能得到最优子结构的解。
如果减小第i个数的区间不包含最大值,那么答案更优;如果包含,最大最小同时减对答案没有影响。

  1. 如果减完后第i个数不是最小值,那么当我们遍历到真正最小值时,能够更新答案得到结果。
  2. 如果减完后第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);
}

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/108165620
今日推荐