不一样的差分+树状数组 Luogu P6912

这么臭的题有必要存在吗?


题意转化

题目所说的“先辈”序列,其实就是一个不下降序列。一个不下降序列,就是“先辈”。这个不再证明。

所以要求的就是:维护一个支持区间加的序列,可以判断区间是否为不下降序列。

实现方式

  1. 我们考虑简单的写法:用线段树维护。显然区间加可以用延迟标记来维护,这里不再赘述。

    一个区间会被线段树分成 O(logn) 个节点。如果这些节点中存在非“先辈”,那么整个区间一定非“先辈”。如果都是先辈,就判断每个节点的右端点是否小于等于下一个节点的左端点。显然线段树也能做到。


但是上述做法常数大,又难写,这里提供一种令人耳目一新的做法。

  1. 可以想到一个序列不降,它的差分序列一定非负。因此我们维护差分序列,是不是简单了很多?显然,区间加变成了单点加。区间查询,变成查询 (l,r] 是否非负。(差分数组,所以要开区间。)

    如何查询整个区间非负呢。容易想到的是查找整个区间的最小值,判断是否大于等于 0。可以用线段树 O(logn) 维护,也可以用树状数组 O(log2n) 维护。

    但是巧妙的地方来了。我们可以用维护一个 01 序列 tr。具体地说,如果差分数组该位置是非负,记为 1;否则记为 0。那么只需判断 i=l+1rtri 是否等于 rl 即可。那么我们可以用一个树状数组维护前缀和得到区间和,常数小而代码短。

代码实现

#include<cstring>
#include<algorithm>
#include<iostream>
#define R myio::read_int()
//在后面的代码中,R 就代表读入。快读函数此处省略。
#define int long long
using namespace std;
const int N=1e6+6;
int n,k,op,l,r,x,c[N],tr[N];
int Sgn(int x){return x>=0;}
void Upd(int x,int d){for(;x<=n;x+=(x&(-x))) tr[x]+=d;}
int GET(int x){int s=0;for(;x;x-=(x&(-x))) s+=tr[x];return s;}
//树状数组维护前缀和
int GetSum(int L,int Ri){return GET(Ri)-GET(L);}
signed main(){
	n=R,k=R;
	for(int i=1,a,bf=0;i<=n;i++) {
		c[i]=(a=R)-bf,bf=a;//c 数组储存差分的实际值
		Upd(i,c[i]>=0?1:0);//初始化树状数组不要忘了
	}while(k--){
		op=R,l=R,r=min(R,n);
		if(op==1){
			x=R;
			Upd(l,Sgn(c[l]+x)-Sgn(c[l]));
			Upd(r+1,Sgn(c[r+1]-x)-Sgn(c[r+1]));
			//判断前后是否出现正负变化
			c[l]+=x,c[r+1]-=x;
		}else {
			if(GetSum(l,r)==r-l) puts("Yes");
			//神之一手,原理请看上文
			else puts("No");
		}
	}
	return 0;
}

实测,这个代码跑得飞快,在众多代码中名列前茅。

思维方式

判断区间是否全满足某一条件时,问题都可以转化成,条件真假的区间和是否等于区间长度。

posted @   robinyqc  阅读(34)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示