P1937/P1607 线段树+贪心

文章讲述了如何使用贪心策略和线段树数据结构解决两个编程题目,涉及到区间最小值的维护、更新和查询,以及二分查找的应用,重点在于算法设计和思维转换。
摘要由CSDN通过智能技术生成

这两道题都是写完第一道题,用第一题的思路转化一下,第二题也能解决,所以放在一起整理

首先是第一题

在这里我就不再证明贪心了,只是直接的取最靠左的位置,这样是最优的(一开始我想的是取短的,后面发现可以hack掉,但是能拿80分

直接贴代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
#define lc u<<1
#define rc u<<1|1
int a[N];
struct Tree{
	int l,r,val,add;//这里直接维护的就是最小值
}tr[N*4];
struct node{
    int x,y;
    int dis;
	bool operator<(const node&t) {return y<t.y;};
}nodes[N];
int n,m;

void pushdown(int u){
	if(tr[u].add){
		tr[lc].add+=tr[u].add;
		tr[rc].add+=tr[u].add;
		tr[lc].val+=tr[u].add;
		tr[rc].val+=tr[u].add;
		tr[u].add=0;
	}
}

void pushup(int u){
	tr[u].val=min(tr[lc].val,tr[rc].val);
}

void build(int u,int l,int r){
	tr[u]={l,r,a[l],0};
	if(l==r) return;
	int m=(l+r)>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	pushup(u);
}

int query(int u,int l,int r){
	if(tr[u].l>=l&&tr[u].r<=r){
		return tr[u].val;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	int minn=1e9;
	if(l<=m) minn=min(query(lc,l,r),minn);
	if(r>m) minn=min(query(rc,l,r),minn);
	return minn;
}

void update(int u,int l,int r,int k){
	if(l<=tr[u].l&&tr[u].r<=r){
		tr[u].val-=1;
		tr[u].add+=k;
		return;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	if(l<=m) update(lc,l,r,k);
	if(r>m) update(rc,l,r,k);
    pushup(u);
}
int main(){
	//维护一颗线段树 直接维护区间最小值,然后每次update让他-1,如果这个最小值小于等于0就直接跳过
	//排序的方法,从短到长且从前到后,贪心,一定最优
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1;i<=m;++i){
		 cin>>nodes[i].x>>nodes[i].y,nodes[i].dis=nodes[i].y-nodes[i].x;
	}
	sort(nodes+1,nodes+1+m);
	build(1,1,n);
	int ans=0;
	for(int i=1;i<=m;++i){
		if(query(1,nodes[i].x,nodes[i].y)>0){
			update(1,nodes[i].x,nodes[i].y,-1);
			ans++;
		}
	}
	cout<<ans<<endl;
	return 0;
}

第二道题思路差不多,要注意维护的区间右端减一就好,题解多是使用维护最大值的区间来计算,这里我用一个小二分+维护最小值也能做,贴一个维护最小值的代码

#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int M=5e4+10;
#define lc u<<1
#define rc u<<1|1
struct Tree{
	int l,r,sum,add;
}tr[M*4];
struct node{
	int x,y,z;
	bool operator<(const node &t)const{return y<t.y;};
}nodes[M];
int k,n,c;
void pushup(int u){
	tr[u].sum=min(tr[lc].sum,tr[rc].sum);
}
void pushdown(int u){
	if(tr[u].add){
		tr[lc].sum+=tr[u].add;
		tr[rc].sum+=tr[u].add;
		tr[lc].add+=tr[u].add;
		tr[rc].add+=tr[u].add;
		tr[u].add=0;
	}
}
void build(int u,int l,int r){
	tr[u]={l,r,c,0};
	if(l==r) return;
	int m=(l+r)>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	pushup(u);
}
void update(int u,int l,int r,int k){
	if(l<=tr[u].l&&tr[u].r<=r){
		tr[u].add+=k;
		tr[u].sum+=k;
		return;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	if(l<=m) update(lc,l,r,k);
	if(r>m) update(rc,l,r,k);
	pushup(u);
}
int query(int u,int l,int r){
	if(l<=tr[u].l&&tr[u].r<=r){
		return tr[u].sum;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	int minn=1e9+10;
	if(l<=m) minn=min(query(lc,l,r),minn);
	if(r>m) minn=min(query(rc,l,r),minn);
	return minn;
}
bool check(int x,int i){
	if(query(1,nodes[i].x,nodes[i].y)<x) return false;
	return true;
}
signed main(){
	cin>>k>>n>>c;
	build(1,1,n);
	for(int i=1;i<=k;++i){
		cin>>nodes[i].x>>nodes[i].y>>nodes[i].z;
		nodes[i].y-=1;
	}
	//for(int i=1;i<=k;++i){
	//	cout<<nodes[i].x<<' '<<nodes[i].y<<' '<<nodes[i].z<<endl;
	//}
	//cout<<endl;
	sort(nodes+1,nodes+1+k);
	int ans=0;
	for(int i=1;i<=k;++i){
		int l=0,r=nodes[i].z+1;
		while(l+1<r){
			int m=(l+r)>>1;
			if(check(m,i)) l=m;
			else r=m;
		}
		update(1,nodes[i].x,nodes[i].y,-l);
		//cout<<l<<endl;
		ans+=l;
	}
	//cout<<endl;
	//for(int i=1;i<=k;++i){
	//	cout<<nodes[i].x<<' '<<nodes[i].y<<' '<<nodes[i].z<<endl;
	//}
	//cout<<endl;
	cout<<ans<<endl;
	return 0;
}

总而言之,实现代码难度不大,重在思维,好像不用线段树也能做话说(,数据有点水

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值