链接:http://ybt.ssoier.cn:8088/problem_show.php?pid=1547
思想
对于一条数链,二分,然后二分,然后再二分…
【题目描述】
给定一数列,规定有两种操作,一是修改某个元素,二是求区间的连续和。(数列元素初始化为0)
【输入】
输入数据第一行包含两个正整数n,m(n≤100000,m≤500000),以下是m行,
每行有三个正整数k,a,b(k=0或1,a,b≤n).k=0时表示将a处数字加上b,k=1时表示询问 区间[a,b]内所有数的和。
【输出】
对于每个询问输出对应的答案。
【输入样例】
10 20
0 1 10
1 1 4
0 6 6
1 4 10
1 8 9
1 4 9
0 10 2
1 1 8
0 2 10
1 3 9
0 7 8
0 3 10
0 1 1
1 3 8
1 6 9
0 5 5
1 1 8
0 4 2
1 2 8
0 1 1
【输出样例】
10
6
0
6
16
6
24
14
50
41
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100005;
int i,j,k;
int read()
{
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-f;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
s=(s<<3)+(s<<1)+ch-'0';
}
return s*f;
}
int n,m;long long c[N*4],a[N];
void update(int k,int l,int r,int x,int y)//在a[l]~a[r]查找a[x]并将其值加上y,再更新其所在线段树的值
{
if(r<x||l>x) return;
if(l=r&&l==x){
c[k]+=y;return;
}
int mid=(l+r)>>1;
update(k*2,l,mid,x,y);
update(k*2+1,mid+1,r,x,y);
c[k]=c[k*2]+c[k*2+1];
}
long long query(int k,int l,int r,int x,int y)//在区间a[l]~a[r]中查找a[x]~a[y]的区间和
{
if(l>y||r<x)return 0;
if(l>=x&&r<=y) return c[k];
int mid=(l+r)>>1;
long long ans=0;
ans+=query(k*2,l,mid,x,y);
ans+=query(k*2+1,mid+1,r,x,y);
return ans;
}
int main()
{
n=read(),m=read();
for(i=1;i<=m;i++)
{
int k1=read(),l=read(),r=read();
if(k1==0)
update(1,1,n,l,r);
else
printf("%lld\n",query(1,1,n,l,r));
}
return 0;
}
附LDX大佬的板子链接:https://blog.csdn.net/dreaming__ldx/article/details/81008642
区间修改区间求和
描述
如题,已知一个数列,你需要进行下面两种操作:(初始化不为0)
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的结果。
样例输入 [复制]
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
样例输出 [复制]
11
8
20
提示
对于70%的数据:N<=1000,M<=10000
对于100%的数据:N<=100000,M<=100000
保证在int64/long long数据范围内
#include<iostream>//标程
#include<cstdio>
#define MAXN 1000001
#define ll long long
using namespace std;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline ll ls(ll x)
{
return x<<1;
}
inline ll rs(ll x)
{
return x<<1|1;
}
void scan()
{
cin>>n>>m;
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
}
inline void push_up(ll p)
{
ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r)
{
tag[p]=0;
if(l==r){ans[p]=a[l];return ;}
ll mid=(l+r)>>1;
build(ls(p),l,mid);
build(rs(p),mid+1,r);
push_up(p);
}
inline void f(ll p,ll l,ll r,ll k)
{
tag[p]=tag[p]+k;
ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r)
{
ll mid=(l+r)>>1;
f(ls(p),l,mid,tag[p]);
f(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k)
{
if(nl<=l&&r<=nr)
{
ans[p]+=k*(r-l+1);
tag[p]+=k;
return ;
}
push_down(p,l,r);
ll mid=(l+r)>>1;
if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p)
{
ll res=0;
if(q_x<=l&&r<=q_y)return ans[p];
ll mid=(l+r)>>1;
push_down(p,l,r);
if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));
return res;
}
int main()
{
ll a1,b,c,d,e,f;
scan();
build(1,1,n);
while(m--)
{
scanf("%lld",&a1);
switch(a1)
{
case 1:{
scanf("%lld%lld%lld",&b,&c,&d);
update(b,c,1,n,1,d);
break;
}
case 2:{
scanf("%lld%lld",&e,&f);
printf("%lld\n",query(e,f,1,n,1));
break;
}
}
}
return 0;
}
#include<bits/stdc++.h>//修正
using namespace std;
#define mid (T[p].l+T[p].r>>1)
const int N=1e5+5;
struct fjy{
long long l,r,lz,sum;
}T[N<<2];//线段树大小开4*n
long long a[N];
inline long long lc(long long x)
{
return x<<1;
}
inline long long rc(long long x)
{
return x<<1|1;
}
long long read1(){
long long s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=getchar();
}
while(isdigit(ch)){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*f;
}
long long read2(){
long long s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-f;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*f;
}
void pushup(int p){
T[p].sum=T[lc(p)].sum+T[rc(p)].sum;
}
void pushnow(int p,long long v){
T[p].sum+=(T[p].r-T[p].l+1)*v;
T[p].lz+=v;
}
void pushdown(int p){//下传p的懒惰标记
pushnow(lc(p),T[p].lz);
pushnow(rc(p),T[p].lz);
T[p].lz=0;
}
void build(int p,int l,int r){
T[p].l=l,T[p].r=r,T[p].sum=0;
if(l==r) {T[p].sum=a[l]; return;}
build(lc(p),l,mid),build(rc(p),mid+1,r);
T[p].sum=T[lc(p)].sum+T[rc(p)].sum;
}
void update(int p,int ql,int qr, long long v){
if(T[p].l>qr||T[p].r<ql) {/*printf("%dfalse ",p);*/ return;}
if(T[p].l>=ql&&T[p].r<=qr) {T[p].sum+=(T[p].r-T[p].l+1)*v,T[p].lz+=v;return;/*记得要返回*/}
pushdown(p);
if(ql<=mid) update(lc(p),ql,qr,v);
if(qr>mid) update(rc(p),ql,qr,v);
pushup(p);
}
long long query(int p,int ql,int qr){
if(T[p].l>qr||T[p].r<ql) return 0;
if(T[p].l>=ql&&T[p].r<=qr) return T[p].sum;
pushdown(p);
long long ant=0;
if(ql<=mid) ant+=query(lc(p),ql,qr);
if(qr>mid) ant+=query(rc(p),ql,qr);
return ant;
}
int n,m,juge,k,x,y;
int main(){
n=read1(),m=read1();
for(int i=1;i<=n;i++) a[i]=read1();
build(1,1,n); // for(int i=1;i<=n*2-1;i++) printf("%d;",T[i].sum);printf("\n");
for(int i=1;i<=m;i++){
juge=read1(),x=read1(),y=read1();
if(juge==1){
k=read1();
update(1,x,y,k); //for(int i=1;i<=n*2-1;i++) printf("%d;",T[i].sum);printf("\n");
}
else
printf("%lld\n",query(1,x,y));
}
return 0;
}
错误总结:
- 数据读入后记得建树(
这是血的教训) - update函数中,若需更新区间不能完全覆盖目标区间,要将目标区间的懒惰标记下传给儿子,在访问子区间(
这也是前人的枯骨总结出的) - update函数中,若需更新区间能完全覆盖目标区间,更新目标区间的sum和lz的值后需要返回
- 线段树的大小要开成n*4