不一样的差分+树状数组 Luogu P6912
这么臭的题有必要存在吗?
题意转化
题目所说的“先辈”序列,其实就是一个不下降序列。一个不下降序列,就是“先辈”。这个不再证明。
所以要求的就是:维护一个支持区间加的序列,可以判断区间是否为不下降序列。
实现方式
-
我们考虑简单的写法:用线段树维护。显然区间加可以用延迟标记来维护,这里不再赘述。
一个区间会被线段树分成
个节点。如果这些节点中存在非“先辈”,那么整个区间一定非“先辈”。如果都是先辈,就判断每个节点的右端点是否小于等于下一个节点的左端点。显然线段树也能做到。
但是上述做法常数大,又难写,这里提供一种令人耳目一新的做法。
-
可以想到一个序列不降,它的差分序列一定非负。因此我们维护差分序列,是不是简单了很多?显然,区间加变成了单点加。区间查询,变成查询
是否非负。(差分数组,所以要开区间。)如何查询整个区间非负呢。容易想到的是查找整个区间的最小值,判断是否大于等于
。可以用线段树 维护,也可以用树状数组 维护。但是巧妙的地方来了。我们可以用维护一个
序列 。具体地说,如果差分数组该位置是非负,记为 ;否则记为 。那么只需判断 是否等于 即可。那么我们可以用一个树状数组维护前缀和得到区间和,常数小而代码短。
代码实现
#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;
}
实测,这个代码跑得飞快,在众多代码中名列前茅。
思维方式
判断区间是否全满足某一条件时,问题都可以转化成,条件真假的区间和是否等于区间长度。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律