You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of op
eration is to add some given number to each number in a given interval. The other is to ask for the
sum of numbers in a given interval.
1.给[a ,b]整体上加上一个常数c。
2.查询[a ,b]区间的和。
Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.
Output
You need to answer all Q commands in order. One answer in a line. The sums may exceed the range of 32-bit integers
Sample Input
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
Sample Output
4
55
9
15
Sol1:Lazy标记
#include<bits/stdc++.h>
using namespace std;
long long sum[800000],n,q,tt[800000],lazy[800000],size[800000],a1,a2,a3;
char s;
void a_lazy(int p,int v) //打Lazy标记操作
{
sum[p]+=size[p]*v; //更新SUM的值,体现加值的操作效果
lazy[p]+=v;
}
void push_down(int p) //下放标记,其与上面那个是两个含义
{
a_lazy(p*2,lazy[p]);
a_lazy(p*2+1,lazy[p]);
lazy[p]=0;
}
void updata(int p)
{
sum[p]=sum[p*2]+sum[p*2+1];
//用左右子结点的SUM值“更新”父亲点的,注意这里是赋值,而不是累加
//好比我们对[1..10]加了5,于是标记只打在[1..10]上,并不下传
//然后对[1..5]加上6,于是加5的标记要传到两个子结点上,然后再将加6的标记传到[1..5】上
//于是得到[1..5]的权值为5*11=55,[6..10]的权值为5*5=25
//于是[1..10]的权值为55+25=80
size[p]=size[p*2]+size[p*2+1];
}
void build(int p,int l,int r)
{
if(l==r)
{
sum[p]=tt[l],size[p]=1;
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);build(p*2+1,mid+1,r);
updata(p);
}
void q_add(int p,int l,int r,int x,int y,int v)
{
if(l>=x&&r<=y) //只有当线段树上区间为当前操作区间的子集时,才打上lazy标记
{
a_lazy(p,v);
return;
}
//如果没有返回的话,说明线段树上的区间大于当前操作的区间
int mid=(l+r)/2;
push_down(p); //此时要将P上的标记,进行下传
if(mid>=x)q_add(p*2,l,mid,x,y,v);
if(mid<y)q_add(p*2+1,mid+1,r,x,y,v);
updata(p); //信息及时上传
}
long long query(int p,int l,int r,int x,int y)
{
if(l>=x&&r<=y) //sum[p]是真实体现这一段数字和的,标记带来的影响已统计进去了
return sum[p];
long long ans=0;
push_down(p);
//此时也要进行标记下传,因为要统计的区间可能从前没有访问过,所以标记根本没有下传, 其sum值也不是真实的
int mid=(l+r)/2;
if(mid>=x)ans+=query(p*2,l,mid,x,y);
if(mid<y)ans+=query(p*2+1,mid+1,r,x,y);
return ans;
}
int main()
{
scanf("%lld%lld",&n,&q);
for(int i=1;i<=n;i++)scanf("%lld",&tt[i]);
build(1,1,n);
for(int i=1;i<=q;i++)
{
cin>>s;
if(s=='Q')
{
scanf("%lld%lld",&a1,&a2);
printf("%lld
",query(1,1,n,a1,a2));
}
else
{
scanf("%lld%lld%lld",&a1,&a2,&a3);
q_add(1,1,n,a1,a2,a3);
}
}
return 0;
}
#include<cstdio>
#define mid (s[x].l+s[x].r>>1)
int n,m,d[100001];
char p[3];
struct oo{int l,r;long long lazy,sum;}s[400001];
void build(int x,int l,int r)
{
s[x].l=l,s[x].r=r;
if(l==r){s[x].sum=d[l];return ;}
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
s[x].sum=s[x<<1].sum+s[x<<1|1].sum;
}
void pushdown(int x) //lazy标记下传
{
int l=x<<1,r=x<<1|1;
s[l].sum+=(s[l].r-s[l].l+1)*s[x].lazy;
s[r].sum+=(s[r].r-s[r].l+1)*s[x].lazy;
s[l].lazy+=s[x].lazy;s[r].lazy+=s[x].lazy;
s[x].lazy=0;
}
void change(int x,int l,int r,int v)
{
if(l<=s[x].l&&r>=s[x].r)
{
s[x].sum+=(s[x].r-s[x].l+1)*v;
s[x].lazy+=v;
return ;
}
if(s[x].lazy) //记得标记下传
pushdown(x);
if(l<=mid)
change(x<<1,l,r,v);
if(r>mid)
change(x<<1|1,l,r,v);
s[x].sum=s[x<<1].sum+s[x<<1|1].sum;
}
long long get(int x,int l,int r)
{
if(l<=s[x].l&&r>=s[x].r)
return s[x].sum;
if(s[x].lazy) //记得标记下传
pushdown(x);
long long ans=0;
if(l<=mid)ans+=get(x<<1,l,r);
if(r>mid)ans+=get(x<<1|1,l,r);
s[x].sum=s[x<<1].sum+s[x<<1|1].sum;
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&d[i]);
build(1,1,n);int x,y,z;
while(m--)
{
scanf("%s",p+1);
if(p[1]=='Q')
{
scanf("%d%d",&x,&y);
printf("%lld
",get(1,x,y));
}
else
{
scanf("%d%d%d",&x,&y,&z);
change(1,x,y,z);
}
}
}
所谓标记永久化,即只在线段树上的区间为当前操作区间的子集时,
才打上标记,且标记并不下发到子结点。
//设树的结构为 p1
// /
// p2
// /
// p3
//当我们有个标记打在p2上面时,则从p1开始走,就事先算出对p1的影响
//由于影响已算出来了,即sum[p1]体现的就是这一段真实的数字和
//于是也就用不着updata上传信息了
//然后走到p2,算出对p2的影响,由于并没有接着向下走
//所以事实上是对p3区间是有影响的,但标记没有下传
//于是在算p3的时候,需要累加从p1到p2这一段的标记量的累加
下面这个代码是Pku3468 A Simple Problem with Integers,代码来自zy
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,m;
ll a[maxn];
ll sum[maxn<<2],add[maxn<<2];
void updata(int p)
{
sum[p]=sum[p<<1]+sum[p<<1|1];
}
void build(int p,int l,int r)
{
if(l==r)
{
sum[p]=a[l];
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
updata(p);
}
void change(int p,int l,int r,int L,int R,ll v)
{
sum[p]+=v*(min(R,r)-max(L,l)+1);
//记录下对区间P的实际贡献
if(L<=l&&r<=R)
{
add[p]+=v;
return;
}
int mid=(l+r)>>1;
if(L<=mid) //如果操作的左边界小于等于mid时,落在左子树
change(p<<1,l,mid,L,R,v);
if(R>mid) //如果操作的右边界大于mid,落在右子树
change(p<<1|1,mid+1,r,L,R,v);
}
ll query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
return sum[p];
int mid=(l+r)>>1;
ll res=add[p]*(min(R,r)-max(L,l)+1);
//累加一路走来,遇到的标记,注意res是个局部变量
if(L<=mid)
res+=query(p<<1,l,mid,L,R);
if(R>mid)
res+=query(p<<1|1,mid+1,r,L,R);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",a+i);
build(1,1,n);
for(int i=1;i<=m;i++) {
int opt,l,r;ll v;
char p[3];
scanf("%s",p+1);
int x,y;scanf("%d%d",&l,&r);
if(p[1]=='C')
{
scanf("%lld",&v);
change(1,1,n,l,r,v);
}
else printf("%lld
",query(1,1,n,l,r));
}
return 0;
}
这个是xyh的
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,m;
ll a[maxn];
struct segment_tree
{
ll sum[maxn<<2],add[maxn<<2];
void updata(int p)
{
sum[p]=sum[p<<1]+sum[p<<1|1];
}
void build(int p,int l,int r)
{
if(l==r) {sum[p]=a[l];return;}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
updata(p);
}
void change(int p,int l,int r,int L,int R,ll v)
{
sum[p]+=v*(R-L+1);
if(L<=l&&r<=R)
{
add[p]+=v;
return;
}
int mid=(l+r)>>1;
if(R<=mid)
change(p<<1,l,mid,L,R,v);
else
if(L>mid)
change(p<<1|1,mid+1,r,L,R,v);
else
change(p<<1,l,mid,L,mid,v),change(p<<1|1,mid+1,r,mid+1,R,v);
}
ll query(int p,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
return sum[p];
int mid=(l+r)>>1;
ll res=add[p]*(R-L+1); //累加一路走来,遇到的标记
if(R<=mid)
res+=query(p<<1,l,mid,L,R);
else
if(L>mid)
res+=query(p<<1|1,mid+1,r,L,R);
else
res+=query(p<<1,l,mid,L,mid)+query(p<<1|1,mid+1,r,mid+1,R);
return res;
}
}T;
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",a+i);
T.build(1,1,n);
for(int i=1;i<=m;i++) {
int opt,l,r;ll v;
char p[3];
scanf("%s",p+1);
int x,y;scanf("%d%d",&l,&r);
if(p[1]=='C')
{
scanf("%lld",&v);
T.change(1,1,n,l,r,v);
}
else printf("%lld
",T.query(1,1,n,l,r));
}
return 0;
}
下面这个是ljq的
//s[p]代表p这一段的数字和,但凡有标记打在p及p的子结点上,都会体现在s[p]上
//但是在p之上结点打过的标记,则不会体现出来。于是要累加一路走下来的标记
//设树的结构为 p1
// /
// p2
// /
// p3
// /
// p4
//设目标有标记打在p2上,则这个影响量开始只算出在p2上面的,
//即s[p2]的值是真实这一段的数字之和
//然后利用updata上传到p1上
//同理,如果有标记打在p3上,也是开始只算出p3的,然后上传到p2,p1上
//由于修改p2,p3,对p4也是有影响的,但标记并没有下传
//于是在算p4的时候,要累加从p1到p3这一段的标记量之和
//标记不下传,只当线段树上区间是目前操作区间的子集时,才打上标记
//这样对于父区间来说,如果标记只打在子区间上,sum的集合是不完整的,于是要updata
//在统计信息的时候,sum代表的时,所有标记打在这个区间上的
//于是因为标记没下传,于是统计要要加上一路走过来时所要累加的量
//例如对[1..10]打上10,[1..5]打上8
//则统计[1..10]的总和时,直接返回sum[1](加上8的那部分在updata时传上来了)
//统计[1..5]时,要返回sum[2]+一路走过的标记*5
#include<cstdio>
#include<iostream>
typedef long long ll;
using namespace std;
ll a[100001];
struct node
{
#define ls (p<<1)
#define rs (p<<1|1)
ll s[400001],cnt[400001];
void updata(int p,int l,int r)
{
s[p]=s[ls]+s[rs]+cnt[p]*(r-l+1);
}
void build(int p,int l,int r)
{
cnt[p]=0;
if(l==r)
{
s[p]=a[l];return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
updata(p,l,r);
}
void change(int p,int l,int r,int x,int y,int v)
//l,r是线段树上的区间
//x,y是当前要操作的
{
if(x<=l&&r<=y) //只有当目前线段树上的区间是要操作的区间的子集时
{
cnt[p]+=v; //加上标记
s[p]+=1LL*(r-l+1)*v;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)change(ls,l,mid,x,y,v);
if(y>mid)change(rs,mid+1,r,x,y,v);
updata(p,l,r);
}
long long ans;
void query(int p,int l,int r,int x,int y,int cc)
{
// printf("%d %d %d %d %d %lld %lld
",p,l,r,x,y,s[p],cnt[p]);
if(x<=l&&r<=y)
{
ans+=s[p];
ans+=1LL*cc*(r-l+1);
return;
}
int mid=(l+r)>>1;
if(x<=mid)query(ls,l,mid,x,y,cc+cnt[p]);
if(y>mid)query(rs,mid+1,r,x,y,cc+cnt[p]);
}
}tree;
int main()
{
int n,q,x,y,z;
char c;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
tree.build(1,1,n);
for(int i=1;i<=q;i++)
{
cin>>c;
scanf("%d%d",&x,&y);
if(c=='Q')
{
tree.ans=0;
tree.query(1,1,n,x,y,0);
printf("%lld
",tree.ans);
}
else
{
scanf("%d",&z);
tree.change(1,1,n,x,y,z);}
}
}
#include<iostream>
#include<cstdio>
#include<cstring>
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 201000
using namespace std;
int n,m;char p[3];
long long sum[N*4],add[N*4];
int a[N];
void build(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=a[l];return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int rt,int l,int r,int v,int xl,int xr)
{
sum[rt]+=v*(min(xr,r)-max(xl,l)+1);
if(l>=xl&&r<=xr) //等到区间完全重合时,再给add加上v增加的标记
{
add[rt]+=v;
return;
}
int mid=(l+r)>>1;
if(xr<=mid)
update(rt<<1,l,mid,v,xl,xr);
else
{
if(xl>mid)
update(rt<<1|1,mid+1,r,v,xl,xr);
else
update(rt<<1,l,mid,v,xl,xr),update(rt<<1|1,mid+1,r,v,xl,xr);
}
}
long long query(int rt,int ad,int l,int r,int xl,int xr)
{
if(xl<=l&&xr>=r)
{
return sum[rt]+ad*(min(xr,r)-max(xl,l)+1);
}
int mid=(l+r)>>1;
if(xr<=mid)
return query(rt<<1,ad+add[rt],l,mid,xl,xr);
else
{
if(xl>mid)
return query(rt<<1|1,ad+add[rt],mid+1,r,xl,xr);
else
return query(rt<<1,ad+add[rt],l,mid,xl,xr)+query(rt<<1|1,ad+add[rt],mid+1,r,xl,xr);
}
}
int main(){
scanf("%d%d",&n,&m);
pos(i,1,n) scanf("%d",&a[i]);
build(1,n,1);
pos(i,1,m)
{
scanf("%s",p+1);
int x,y;scanf("%d%d",&x,&y);
if(p[1]=='C')
{
int k;scanf("%d",&k);
update(1,1,n,k,x,y);
}
else printf("%lld
",query(1,0,1,n,x,y));
}
return 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的结果。
输入输出样例
输入样例#1:
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出样例#1:
11
8
20
说明
时空限制:1000ms,128M
数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=1000,M<=10000
对于100%的数据:N<=100000,M<=100000
(数据已经过加强_,保证在int64/long long数据范围内)
#include<cstdio>
#include<iostream>
using namespace std;
const int N=300010;
struct node{
int ls,rs,lazy;
long long sum;
}tr[N];
int root=0,cnt=0;
void insert(int &root,int l,int r,int ll,int rr,int x)
{
if(!root) //动态开点
root=++cnt;
int b=min(r,rr)-max(l,ll)+1;
tr[root].sum+=b*x;
if(l>=ll&&r<=rr)
{
tr[root].lazy+=x;
return;
}
int mid=l+r>>1;
if(ll<=mid)
insert(tr[root].ls,l,mid,ll,rr,x);
if(rr>mid)
insert(tr[root].rs,mid+1,r,ll,rr,x);
}
long long query(int root,int l,int r,int ll,int rr)
{
if(l>=ll&&r<=rr)
return tr[root].sum;
int mid=l+r>>1;
if(tr[root].lazy) //lazy标记下传
{
if(!tr[root].ls)
tr[root].ls=++cnt;
tr[tr[root].ls].lazy+=tr[root].lazy;
tr[tr[root].ls].sum+=tr[root].lazy*(mid-l+1);
if(!tr[root].rs)
tr[root].rs=++cnt;
tr[tr[root].rs].lazy+=tr[root].lazy;
tr[tr[root].rs].sum+=tr[root].lazy*(r-mid);
tr[root].lazy=0;
}
long long ans=0;
if(ll<=mid)
ans+=query(tr[root].ls,l,mid,ll,rr);
if(rr>mid)
ans+=query(tr[root].rs,mid+1,r,ll,rr);
return ans;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int temp;
cin>>temp;
insert(root,1,n,i,i,temp);
}
for(int i=1;i<=m;i++)
{
int ta,tb,tc,td;
cin>>ta>>tb>>tc;
if(ta==1)
{
cin>>td;
insert(root,1,n,tb,tc,td);
}
else
if(ta==2)
{
cout<<query(root,1,n,tb,tc)<<endl;
}
}
return 0;
}