P2184 贪婪大陆(习题笔记)

文章讲述了如何通过类差分思想和前缀和处理策略解决Luogu竞赛中的P2184贪婪大陆问题,主要关注区间修改转化为端点操作的方法,以及使用C++实现的更新和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P2184

这道题一开始写的时候完全没想到解法。没有结合其他算法,算是一个思维题,运用了类差分思想和前缀和思想的处理方式。

这道题的核心在于区间修改。但是我们发现没办法操控修改完之后怎么表示(不能用和的形式)

这里考虑将区间问题转化成端点操作问题

以下图示摘自董晓老师的博客

这样一来,局势就瞬间明了了。

这道题对3-5查询 实际上就是查询1-5的起点减去1-2的终点(前缀和思想)

更实质的说,这里是让1-5所有存在的线段减去在3之前就结束的

起点说明有这一段(因为只有端点,所以不会覆盖),终点说明没有这一段

我的印象中,这道题很经典(以前好像也见过但是没解出来),浅浅记下

贴代码(过于暴力

// Problem: 
//     P2184 贪婪大陆
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2184
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
using namespace std;
int n,m;
const int N=1e5+10;
#define int long long
struct node{
	int l,r,suml,addl,sumr,addr;
}tr[N*4];
#define lc u<<1
#define rc u<<1|1
void pushup(int u){
    tr[u].sumr=tr[lc].sumr+tr[rc].sumr;
    tr[u].suml=tr[lc].suml+tr[rc].suml;
}
void pushdown(int u){
	if(tr[u].addl){
		tr[lc].suml+=tr[u].addl*(tr[lc].r-tr[lc].l+1);
		tr[rc].suml+=tr[u].addl*(tr[rc].r-tr[rc].l+1);
		tr[lc].addl+=tr[u].addl;
		tr[rc].addl+=tr[u].addl;
		tr[u].addl=0;
	}
	if(tr[u].addr){
		tr[lc].sumr+=tr[u].addr*(tr[lc].r-tr[lc].l+1);
		tr[rc].sumr+=tr[u].addr*(tr[rc].r-tr[rc].l+1);
		tr[lc].addr+=tr[u].addr;
		tr[rc].addr+=tr[u].addr;
		tr[u].addr=0;
	}
	
}
void build(int u,int l,int r){
	tr[u]={l,r};
	if(l==r) return;
	int m=(l+r)>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	pushup(u);
}
void updatel(int u,int l,int r,int k){
	if(l<=tr[u].l&&tr[u].r<=r){
		tr[u].suml+=(tr[u].r-tr[u].l+1)*k;
		tr[u].addl+=k;
		return;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	if(l<=m) updatel(lc,l,r,k);
	if(r>m) updatel(rc,l,r,k);
	pushup(u);
}
void updater(int u,int l,int r,int k){
	if(l<=tr[u].l&&tr[u].r<=r){
		tr[u].sumr+=(tr[u].r-tr[u].l+1)*k;
		tr[u].addr+=k;
		return;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	if(l<=m) updater(lc,l,r,k);
	if(r>m) updater(rc,l,r,k);
	pushup(u);
}
int queryl(int u,int l,int r){
	if(l<=tr[u].l&&tr[u].r<=r){
		 return tr[u].suml;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	int num=0;
	if(l<=m) num=queryl(lc,l,r);
	if(r>m) num+=queryl(rc,l,r);
	return num;
}
int queryr(int u,int l,int r){
	if(l<=tr[u].l&&tr[u].r<=r){
		 return tr[u].sumr;
	}
	pushdown(u);
	int m=(tr[u].l+tr[u].r)>>1;
	int num=0;
	if(l<=m) num=queryr(lc,l,r);
	if(r>m) num+=queryr(rc,l,r);
	return num;
}

signed main(){
	cin>>n>>m;
	build(1,1,n);
	while(m--){
		int op,a,b;cin>>op>>a>>b;
		if(op==1){
			updatel(1,a,a,1);
			updater(1,b,b,1);
		}
		else{
			cout<<queryl(1,1,b)-queryr(1,1,a-1)<<endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值