复杂度O(m log n)、以下四个题目难度依次递增。
Code1:单点修改,查询区间和 (基础入门版)
#include
#include
#include
#include
using namespace std;
const int N = 1e5+7;
int w[N];
struct node{ //树节点结构,节点编号为下标,u<<1 为左子树,u<<1|1为右子树 (与堆的存储一样)
int l, r;
int sum;
}tr[4*N]; // 叶子节点(N)+ 其他父节点(3N)
void pushup(int u) //更新节点信息,这里是求和
{
tr[u].sum = tr[u<<1].sum +tr[u<<1|1].sum;
}
void build(int u,int l,int r) //建树
{
if(l==r) tr[u]={l,r,w[l]}; // 叶子节点赋初始值
else {
tr[u] = {l,r}; //初始值
int mid = l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r); //构建左右子树
pushup(u); //叶子节点变化,更新父节点信息
}
}
int query(int u,int l,int r) //查询函数,这里是求区间和
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum ; //目标区间的子区间
int mid = tr[u].l + tr[u].r >> 1;
int ans =0 ;
if(l <= mid) ans+=query(u<<1, l,r); //递归左右子树查询
if(r>=mid+1) ans += query(u<<1|1,l,r);
return ans;
}
void modify(int u,int x,int v) //节点信息修改
{
if(tr[u].l == tr[u].r ) tr[u].sum += v; //修改叶子节点信息
else {
int mid = tr[u].l + tr[u].r >>1;
if(x <= mid) modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u); //由于叶子节点信息变化,更新父节点
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&w[i]); //读入叶子节点权值
build(1,1,n); //建树
while(m--){
int k,a,b;
scanf("%d%d%d",&k,&a,&b);
if(k==0) printf("%d\n",query(1,a , b)); //查询区间和
else modify(1, a, b); //修改指定叶子节点权值;
}
return 0;
}
Code2:单点修改,查询区间内最大连续子段和 (维护多个信息)
#include
#include
using namespace std;
const int N = 5e5+7;
int w[N];
struct Node{
int l,r;
int tmax,lmax,rmax,sum;
}tr[N*4];
void pushup(Node &u,Node &l,Node &r){
u.sum = l.sum+r.sum;
u.lmax = max(l.lmax,l.sum+r.lmax); //区间前缀最大连续子段和
u.rmax = max(r.rmax,r.sum+l.rmax); //区间后缀最大连续子段和
u.tmax = max(max(l.tmax,r.tmax),l.rmax+r.lmax); //区间最大连续子段和
}
void pushup(int u){
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
if(l==r) tr[u] = {l,r,w[l],w[l],w[l],w[l]};
else {
tr[u] = {l,r};
int mid = l + r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int x,int v)
{
if(tr[u].l==x &&tr[u].r ==x) tr[u] = {x,x,v,v,v,v};
else {
int mid = tr[u].l+tr[u].r>>1;
if(x<=mid) modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u);
}
}
Node query(int u,int l,int r)
{
if(tr[u].l>=l && tr[u].r<=r) return tr[u];
int mid = tr[u].l + tr[u].r >>1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else {
Node ans,left,right;
left = query(u<<1,l,r);
right = query(u<<1|1,l,r);
pushup(ans,left,right);
return ans;
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
int k,x,y;
while(m--){
scanf("%d%d%d",&k,&x,&y);
if(k==1) {
if(x>y) swap(x,y);
printf("%d\n",query(1,x,y).tmax);
}else modify(1,x,y);
}
return 0;
}
Code3:区间加,查询区间和 (单个懒标记)
#include
#include
using namespace std;
const int N = 1e5+7;
typedef long long LL;
int w[N];
struct Node{
int l,r;
LL sum,add;
}tr[N*4];
void pushup(int u){
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1]; //引用
if(root.add){
left.add+=root.add,left.sum+=(LL)(left.r-left.l+1)*root.add; //由父节点更新子节点
right.add+=root.add,right.sum+=(LL)(right.r-right.l+1)*root.add;
root.add=0; //去除父节点懒标记
}
}
void build(int u,int l,int r)
{
if(l==r) tr[u] = {l,r,w[l],0};
else {
tr[u] = {l,r};
int mid = l + r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int v)
{
if(tr[u].l>=l && tr[u].r <= r) { //此节点代表的区间在目标区间里
tr[u].sum += (LL)(tr[u].r-tr[u].l+1)*v;
tr[u].add += v; //懒标记
}else{
pushdown(u); //每次分裂前要pushdown一下
int mid = tr[u].l +tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,v); //左边交集
if(r>mid) modify(u<<1|1,l,r,v); //右边交集
pushup(u);
}
}
LL query(int u,int l,int r)
{
if(tr[u].l>=l && tr[u].r<=r) return tr[u].sum; //此节点代表的区间在目标区间里
pushdown(u); //分裂前
LL ans=0;
int mid = tr[u].l + tr[u].r>>1;
if(l<=mid) ans += query(u<<1,l,r); //左边有交集
if(r>mid) ans += query(u<<1|1,l,r); //右边有交集
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
while(m--){
int x,l,r,d;
scanf("%d%d%d",&x,&l,&r);
if(x==2) printf("%lld\n",query(1,l,r));
else scanf("%d",&d) , modify(1,l,r,d);
}
return 0;
}
Code4:区间乘、区间加,查询区间和(双重懒标记)
#include
using namespace std;
typedef long long LL;
const int N = 1e5+7;
int n,m,p;
int w[N];
struct Node{
int l,r;
int sum,add,mul;
}tr[N*4];
void pushup(int u){
tr[u].sum = (tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
void eval(Node &t,int add,int mul) //引用
{
t.sum = ((LL)t.sum*mul + (LL)(t.r-t.l+1)*add) % p;
t.mul = (LL)t.mul*mul%p; //采取先乘后加,方便把懒标记pushdown给儿子
t.add = ((LL)t.add*mul+add)%p; // 公式:(sum*a+b)*c+d == sum*ac+bc+d
} //其中 sum*a+b为儿子的标记
void pushdown(int u)
{
eval(tr[u<<1],tr[u].add,tr[u].mul); //给儿子更新状态
eval(tr[u<<1|1],tr[u].add,tr[u].mul);
tr[u].add=0,tr[u].mul=1; //清除标记
}
void build(int u,int l,int r)
{
if(l==r) tr[u] = {l,r,w[l],0,1};
else {
tr[u] = {l,r,0,0,1};
int mid = l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int add,int mul)
{
if(tr[u].l>=l && tr[u].r<=r) eval(tr[u],add,mul); //目标区间的子集,更新懒标记
else {
pushdown(u);
int mid = tr[u].l + tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,add,mul);
if(r>mid) modify(u<<1|1,l,r,add,mul);
pushup(u);
}
}
int query(int u,int l,int r)
{
if(tr[u].l>=l && tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r>>1;
int ans=0;
if(l<=mid) ans =(ans + query(u<<1,l,r))%p;
if(r>mid) ans =(ans + query(u<<1|1,l,r))%p;
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
int x,l,r,d;
while(m--){
scanf("%d%d%d",&x,&l,&r);
if(x==3) printf("%d\n",query(1,l,r));
else {
scanf("%d",&d);
if(x==1) modify(1,l,r,0,d);
else modify(1,l,r,d,1);
}
}
return 0;
}