( Lazy 线段树) + 01背包(滚动数组)

做法2:使用懒惰标记,每次修改都对懒惰标记进行操作,每次到达一个节点都 tag[o] 标记下传更新 tree[ls/rs] 的权值,tag[o]标记还原1e9,就可以快速获取每个物品的最小重量。链接:https://ac.nowcoder.com/acm/contest/917/E
来源:牛客网

希望

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld

题目描述

题目背景

那一瞬间,一道白光闪过,从此大地上再也没有了丝毫的响动

那是一个黑暗的晚上。

新纪元59β年,23:13:56,这个悲惨的时刻。

所有人都开始自相残杀,亲人、朋友,都是敌人。

终有人类逃出了这片黑暗的天地,试图挽救这悲惨的未来。

但是腐化生物的能力完全超出了人类的控制范围。

勇者号的主控台与舰桥,人类最后与腐化生物抗衡的希望。

很可惜,α计划失败了,腐化生物即将入侵主控台。人类的最后英雄试图反抗。

这一切都是徒劳。

不!还有最后的希望!毁灭号的等离子充能炮!

如果不能得到,就将他彻底毁掉。

新纪元596α,下起了倾盆大雨,哭诉着勇者号自豪去死的英雄。

题目描述

水宝宝驾驶着毁灭号接近了勇者号舰桥。

他要使用毁灭号的等离子炮摧毁勇者号主控台。

但是操控等离子炮的程序出了点问题。等离子炮有n个操作信号,第i个操作信号的强度为b[i]。总体强度为各操作信号的强度之和。

由于有些信号太弱了了 (强度<0),水宝宝想把它们删除。但是水宝宝自己不会删除信号,所以他找来了同船的队友帮忙。

有 m位队友,第ii 位队友只会删除编号在 L[i] 和 R[i]之间的信号,且每删除一个信号,花费 C[i]格能量。飞船一共有 k格能量,问他在请队友删除完信号后,总体强度最大是多少。

注:本系列题不按难度排序哦

输入描述:

输入格式:

第一行包含三个正整数 n,k,m

第二行包含 n个正整数 b1,b2,⋯,bn,表示各信号的强度。

接下来 m 行,每行三个正整数Li,Ri,Ci,表示一个队友的属性。

输出描述:

输出格式:

输出一行一个整数,表示最大的信号强度

示例1

输入

复制

5 10 5
10 -2 -5 7 -10
1 1 5
2 4 10
4 4 12
3 4 10
1 5 15

输出

复制

5

说明

样例解释:花费10的代价除掉a[3],答案即为10+7-10-2=5

备注:

对于 100% 的数据,1≤n,m≤10^5;1≤k≤500;1≤C[i]≤500;1≤L[i]≤R[i]≤n;-10^9≤b[i]≤10^9

新纪596α 12:00:14水宝宝用沾满汗水的双手拉动了操纵杆,将等离子炮对准了勇者号主控台。

“再见。勇士们。”水宝宝向勇者号发送了它能接收的最后一条信息。

水宝宝用微颤的手摁下了Enter。

一声巨响后,世界重归宁静。

英雄们倒在了炮火里,没有痛苦。

整个世界只剩毁灭号引擎的轰鸣。

如果失去了勇者号,我们只能孤注一掷。

人类最后的希望,R8IO核弹。

北半球的天空,从此只有黑夜。

做法1:最多n个物品,对l~r之间的物品重量 用线段树进行区间修改,并查询每个合法物品的最小重量,然后用滚动数组 01背包问题dp,即可得出答案。

#include<bits/stdc++.h> 
#define ls o*2
#define rs o*2+1
#define mid (l+r)/2
using namespace std;
struct node
{
	int v,w;
};
int a[100005];
vector<node> aa;
int dp[505]={0};
int cnt=1;
int n,k,m;
int sum=0;
vector<int> que;
int tree[4*100005];
void up(int o,int l,int r,int ql,int qr,int v)
{
	if(ql<=l&&r<=qr)
	{
		tree[o]=min(tree[o],v);//不断对一个节点更新最小值 
		return ;
	}
	if(ql<=mid) up(ls,l,mid,ql,qr,v);
	if(mid<qr) up(rs,mid+1,r,ql,qr,v);
	return ;
	
}
int qu(int o,int l,int r,int ql,int qr)
{
	if(l==r) return tree[o];
	if(ql<=mid) return min(tree[o],qu(ls,l,mid,ql,qr));//不断取当前路径上的最小值 
	else return min(tree[o],qu(rs,mid+1,r,ql,qr));
}
int main()
{
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=n;i++)
	{
		int v;
		scanf("%d",&a[i]);
		sum+=a[i];
	}
	for(int i=1;i<=4*100005;i++) tree[i]=1e9;//每个节点初始化为无穷大 
	for(int i=0;i<m;i++)
	{
		int a,ab,w;
		scanf("%d%d%d",&a,&ab,&w);
		up(1,1,n,a,ab,w);
	}
	for(int i=1;i<=n;i++)
	{
		if(a[i]<0)		
		{
			node cc;
			cc.v=-1*a[i];
			cc.w=qu(1,1,n,i,i);
			aa.push_back(cc);
			
		}
	}
	int ans=0;
	cnt=aa.size();
	for(int i=0;i<cnt;i++)
	{
		for(int j=k;j>=0;j--)
		{			
			if(j>=aa[i].w)dp[j]=max(dp[j],dp[j-aa[i].w]+aa[i].v);//01背包 使用一维数组 / 滚动数组		
			if(j==k) ans=max(ans,dp[k]);
		}
	}
	printf("%d\n",sum+ans);
}

做法2:使用懒惰标记,每次修改都对懒惰标记进行操作,每次到达一个节点都 tag[o] 标记下传更新 tree[ls/rs] 的权值,tag[o]标记还原1e9,就可以快速获取每个物品的最小重量。 

#include<bits/stdc++.h> 
#define ls o*2
#define rs o*2+1
#define mid (l+r)/2
using namespace std;
struct node
{
	int v,w;
};
int a[100005];
vector<node> aa;
int dp[505]={0};
int cnt=1;
int n,k,m;
int sum=0;
vector<int> que;
int tree[4*100005],tag[4*100005];
void pushdown(int o) 
{
	if(tag[o]!=1e9)//标记有意义则下传 
	{
		tag[ls]=min(tag[ls],tag[o]);//标记下传 
		tag[rs]=min(tag[rs],tag[o]);
			
		tree[ls]=min(tree[ls],tag[ls]);//对子节点修改!!!!
		tree[rs]=min(tree[rs],tag[rs]);//不是对当前节点修改!!! 
		tag[o]=1e9;//初始化标记 
	}
}
void up(int o,int l,int r,int ql,int qr,int v)
{
	if(ql<=l&&r<=qr)
	{
		tree[o]=min(tree[o],v);//不断对一个节点更新最小值 
		tag[o]=min(tag[o],v);//标记修改 
		return ;
	}
	pushdown(o);//下传函数位置!!! 
	if(ql<=mid) up(ls,l,mid,ql,qr,v);
	if(mid<qr) up(rs,mid+1,r,ql,qr,v);
	return ;
	
}
int qu(int o,int l,int r,int ql,int qr)
{
	if(l==r) return tree[o];
	pushdown(o);//传函数位置!!! 
	if(ql<=mid)
	{
		return qu(ls,l,mid,ql,qr);	
	}
	else 
	{
		return qu(rs,mid+1,r,ql,qr);
	}
}
int main()
{
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=n;i++)
	{
		int v;
		scanf("%d",&a[i]);
		sum+=a[i];
	}
	for(int i=1;i<=4*100005;i++) tree[i]=1e9,tag[i]=1e9;//每个节点初始化为无穷大 
	for(int i=0;i<m;i++)
	{
		int a,ab,w;
		scanf("%d%d%d",&a,&ab,&w);
		up(1,1,n,a,ab,w);
	}
	for(int i=1;i<=n;i++)
	{
		if(a[i]<0)		
		{
			node cc;
			cc.v=-1*a[i];
			cc.w=qu(1,1,n,i,i);
			aa.push_back(cc);
			
		}
	}
	int ans=0;
	cnt=aa.size();
	for(int i=0;i<cnt;i++)
	{
		for(int j=k;j>=0;j--)
		{			
			if(j>=aa[i].w)dp[j]=max(dp[j],dp[j-aa[i].w]+aa[i].v);//01背包 使用一维数组 / 滚动数组		
			if(j==k) ans=max(ans,dp[k]);
		}
	}
	printf("%d\n",sum+ans);
}

 

发布了44 篇原创文章 · 获赞 6 · 访问量 1185

猜你喜欢

转载自blog.csdn.net/qq_43868883/article/details/102731743