L3-017 森森快递(线段树+贪心)

题目链接:题目详情 - L3-017 森森快递 (pintia.cn)

 样例输入:

10 6
0 7 8 5 2 3 1 9 10
0 9
1 8
2 7
6 3
4 5
4 2

样例输出:

7

分析:题意就是让我们选取若干个不相交区间,使得每个区间的最小值的和最大。我们先来分析一下假如两个区间是相互包含的关系,那么我们应该怎样选取,可以发现我们应该选取小区间,原因很简单,因为小区间如果和其他区间冲突,那么大区间一定冲突,且小区间的区间最小值一定大于等于所包含这个小区间的大区间的区间最小值,所以我们可以考虑先对区间进行排序,排序方法就是按照r从小到大排序,r越小说明对后续区间产生冲突的可能性更小,所以更应该优先选取,而且如果后续有更优答案还可以替换掉该区间,我们可以把n个城市之间的线路看成n-1个点,那么第i个点的初始点权就是第i个点到第i+1个点所能承受的最大权重,然后我们按照排序后的路线进行询问,每次询问当前路线上的点的最小值,然后让该路线上的所有点权都减去这个最小值,答案加上这个最小值即可。这其实是一个逐步替换的过程,在选定当前线路之前我们难免会选择一些不太优的线路,如果当前区间的最小值不为0,那么我们还可以用当前线路去替换掉那些不太优的线路来使得答案更优,前提是不太优的区间是不能完全包含更优的区间,要不然有可能出现大区间最小值和小区间最小值重合从而使得小区间无法替换大区间,由于涉及到区间修改和区间查询,所以我们可以用线段树实现。

不懂线段树的小伙伴可以看下这里:线段树模板_AC__dream的博客-CSDN博客

细节见代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e6+10;
long long l[N],r[N],mn[N],lazy[N];
struct node{
	long long l,r;
}p[N];
void pushup(int id)
{
	mn[id]=min(mn[id<<1],mn[id<<1|1]);
}
void pushdown(int id)
{
	if(lazy[id])
	{
		mn[id<<1]+=lazy[id];
		mn[id<<1|1]+=lazy[id];
		lazy[id<<1]+=lazy[id];
		lazy[id<<1|1]+=lazy[id];
		lazy[id]=0;
	}
}
void build(int id,int L,int R)
{
	l[id]=L;r[id]=R;lazy[id]=0;
	if(L==R)
	{
		scanf("%lld",&mn[id]);
		return ;
	}
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
	pushup(id);
}
void update_interval(int id,int L,int R,long long val)
{
	if(l[id]>=L&&r[id]<=R)
	{
		mn[id]+=val;
		lazy[id]+=val;
		return ;
	}
	pushdown(id);
	int mid=l[id]+r[id]>>1;
	if(L<=mid) update_interval(id<<1,L,R,val);
	if(mid+1<=R) update_interval(id<<1|1,L,R,val);
	pushup(id);
}
long long query_interval(int id,int L,int R)
{
	if(l[id]>=L&&r[id]<=R) return mn[id];
	pushdown(id);
	long long ans=0x3f3f3f3f3f3f3f3f;
	int mid=l[id]+r[id]>>1;
	if(L<=mid) ans=min(ans,query_interval(id<<1,L,R));
	if(mid+1<=R) ans=min(ans,query_interval(id<<1|1,L,R));
	return ans;
}
bool cmp(node a,node b)
{
	return a.r<b.r;
}
int main()
{
	int n,q;
	cin>>n>>q;
	n--;
	build(1,1,n);
	for(int i=1;i<=q;i++)
	{
		scanf("%lld%lld",&p[i].l,&p[i].r);
		if(p[i].l>p[i].r) swap(p[i].l,p[i].r);
		p[i].l++;//默认坐标从1开始 
	}
	sort(p+1,p+q+1,cmp);
	long long ans=0;
	for(int i=1;i<=q;i++)
	{
		int t=query_interval(1,p[i].l,p[i].r);
		ans+=t;
		update_interval(1,p[i].l,p[i].r,-t);
	}
	printf("%lld\n",ans);
	return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值