线段树板子
线段树练习
原问题:线段树模板1
题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和
输入输出格式
输入格式:
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和
输出格式:
输出包含若干行整数,即为所有操作2的结果。
代码(数组)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int Nmax=100010;
const int Mmax=100010;
inline int lp(int p)
{
return p<<1;
}
inline int rp(int p)
{
return p<<1|1;
}
struct Sg{
int l,r;
int p;
long long data,add;
Sg()
{
add=0;
}
};
Sg T[Nmax*4];
int a[Nmax];
#define data(p) T[p].data
#define l(p) T[p].l
#define r(p) T[p].r
#define add(p) T[p].add
int M,N;
inline int len(int a,int b)
{
return b-a+1;
}
void build(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(l==r)
{
data(p)=a[l];
return;
}
int mid=(l+r)>>1;
build(lp(p),l,mid);
build(rp(p),mid+1,r);
data(p)=data(p*2)+data(p*2+1);
return;
}
void spread(int p)
{
if(add(p))
{
data(lp(p))+=add(p)*len(l(lp(p)),r(lp(p)));
data(rp(p))+=add(p)*len(l(rp(p)),r(rp(p)));
add(lp(p))+=add(p);
add(rp(p))+=add(p);
add(p)=0;
}
}
void update(int p,int l,int r,int x)
{
if(l<=l(p) && r(p)<=r)
{
data(p)+=(long long)x*(len(l(p),r(p)));
add(p)+=x;
return;
}
spread(p);
int mid=(l(p)+r(p))>>1;
if(l<=mid)
update(lp(p),l,r,x);
if(mid<r)
update(rp(p),l,r,x);
data(p)=data(lp(p))+data(rp(p));
return;
}
long long ask(int p,int l,int r)
{
if(l<=l(p) && r(p)<=r)
return data(p);
spread(p);
int mid=(l(p)+r(p))>>1;
long long tmp=0;
if(l<=mid)
tmp+=ask(lp(p),l,r);
if(mid<r)
tmp+=ask(rp(p),l,r);
return tmp;
}
int main()
{
scanf("%d %d",&N,&M);
for(register int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
}
build(1,1,N);
for(register int i=1;i<=M;i++)
{
int c,x,y,k;
scanf("%d %d %d",&c,&x,&y);
if(c==1)
{
scanf("%d",&k);
update(1,x,y,k);
}
else if(c==2)
{
printf("%lld\n",ask(1,x,y));
}
}
return 0;
}