A Simple Problem with Integers
题目大意:
poj 3468
给出N个数,进行Q个操作,1<=N,Q<=1e5.有两种操作:
“C a b”,对区间[a,b]的每个数组加c;
“Q a b”查询区间[a,b]的数字和。
输入:N,Q,以及N个数字,Q个操作。
输出:对每个查询的操作,输出结果。
样例输入:
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
输出:
4
55
9
15
解析:
复杂度分析
容易知道暴力的话时间复杂度是 O ( n 2 ) O(n^2) O(n2) 。区间修改在线段树中如果一个点一个点修改的话,对一个点的修改是 l o g ( n ) log(n) log(n),对区间上的每个点修改就是 n ∗ l o g ( n ) n*log(n) n∗log(n),有q次操作那么一共就是 n 2 ∗ l o g ( n ) n^2*log(n) n2∗log(n),还不如暴力法。
lazy-tag的方法:
当我们要给一个区间的值都加上c的时候,我们不去修改这个区间里面所有的点,反而给这个一整个加上c而不向下深入。当某一次查询或者更新的时这个区间被破环时就要向下更新。
更新操作:
比如你想给区间[3,6]里的数都加上一个数,那么对于区间[3,6],从根节点开始,递归查找区间[3,6] 有两种情况:与[3,6]交错,被[3,6] 包含,对于第二种就把这个区间上的和sum加上
c
∗
(
r
−
l
+
1
)
c*(r-l+1)
c∗(r−l+1),用一个add数组标记lazy标签,其值加上c。(为什么这样加看下面代码注解)。
对于第1种要继续深入,比如对于区间[6,10],它与区间[3,6]交错,如果区间[6,10]有lazy标签,那么就需要向下更新(因为在之前的某一次操作中子区间并没有更新,而现在需要向下深入)。
查询操作:
比如对于查询区间[3,6]的区间和,那么对于区间[3,6],同样从根节点开始,递归查找区间[3,6] 同样有两种情况:与[3,6]交错,被[3,6] 包含,对于第一种那么就继续深入,同样可能引发向下更新。对于第二种区间直接返回这个区间的和,而不继续深入。
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstdio>
#include<string>
#include<string.h>
#include<list>
#include<queue>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<deque>
#include<stack>
using namespace std;
#define debug(x) cout<<"###"<<x<<"###"<<endl;
const int INF=0x3f3f3f3f,mod=1e9,Maxn=4*1e6;//空间要4倍以免RE
typedef long long ll;
ll sum[Maxn],add[Maxn];//sum 区间和,add lazy标记
ll a[Maxn];
int n,cnt=1;
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
void push_up(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];//向上更新,把子区间的信息向上传递
}
void push_down(int rt,int m){//说明区间被有交错,也就是lazy标签被破坏 m这个区间的长度
if(add[rt]){//如果这个区间有lazy标签
add[rt<<1]+=add[rt];// 传递标签给左孩子
add[rt<<1|1]+=add[rt];//传递标签给右孩子
sum[rt<<1]+=(m-(m>>1))*add[rt];//左孩子的sum更新,add加c的作用就是方便孩子sum的更新。
sum[rt<<1|1]+=(m>>1)*add[rt];//右孩子的sum更新
add[rt]=0; //取消本层的标记
}
}
void build(int l,int r,int rt){//建树
add[rt]=0;
if(l==r){
sum[rt]=a[cnt];
cnt++;
return;
}
int mid=(l+r)>>1;
build(lson);
build(rson);
push_up(rt);//向上更新返回区间和
}
void update(int a,int b,ll c,int l,int r,int rt){//更新区间
if(a<=l&&b>=r){
sum[rt]+=(r-l+1)*c;//区间被包含整体sum加上(r-l+1)*c
add[rt]+=c;//加上c标记
return;
}
push_down(rt,r-l+1);//向下更新
int mid=(l+r)>>1;
if(a<=mid){//分成两半继续深入
update(a,b,c,lson);
}
if(b>mid){
update(a,b,c,rson);
}
push_up(rt);//向上更新
}
ll query(int a,int b,int l,int r,int rt){
if(a<=l&&b>=r)return sum[rt];//如果区间被包括。直接返回
push_down(rt,r-l+1);//否则说明区间被破坏,有可能需要更新
int mid=(l+r)>>1;//分成两半继续向下查找
ll ans=0;
if(a<=mid){
ans+=query(a,b,lson);
}
if(b>mid)ans+=query(a,b,rson);
return ans;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
while(m--){
char s[2];
int a,b;
ll c;
scanf("%s",s);
if(s[0]=='C'){
scanf("%d%d%lld",&a,&b,&c);
update(a,b,c,1,n,1);//更新
}else{
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
}
return 0;
}
复杂度分析:
对于一个区间的查询与更新,复杂度是 l o g ( n ) log(n) log(n),有q次操作那么总的复杂度就是 n l o g ( n ) nlog(n) nlog(n)。
同时上面那个代码也是一个模板代码,很多题改一下就行了。
没有注释的板子
#include<iostream>
#include<algorithm>
#include<math.h>
#include<cstdio>
#include<string>
#include<string.h>
#include<list>
#include<queue>
#include<sstream>
#include<vector>
#include<set>
#include<map>
#include<deque>
#include<stack>
using namespace std;
#define debug(x) cout<<"###"<<x<<"###"<<endl;
const int INF=0x3f3f3f3f,mod=1e9,Maxn=4*1e6;
typedef long long ll;
ll sum[Maxn],add[Maxn];
ll a[Maxn];
int n,cnt=1;
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
void push_up(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int rt,int m){
if(add[rt]){
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
sum[rt<<1]+=(m-(m>>1))*add[rt];
sum[rt<<1|1]+=(m>>1)*add[rt];
add[rt]=0;
}
}
void build(int l,int r,int rt){
add[rt]=0;
if(l==r){
sum[rt]=a[cnt];
cnt++;
return;
}
int mid=(l+r)>>1;
build(lson);
build(mid+1,r,rt<<1|1);
push_up(rt);
}
void update(int a,int b,ll c,int l,int r,int rt){
if(a<=l&&b>=r){
sum[rt]+=(r-l+1)*c;
add[rt]+=c;
return;
}
push_down(rt,r-l+1);
int mid=(l+r)>>1;
if(a<=mid){
update(a,b,c,lson);
}
if(b>mid){
update(a,b,c,mid+1,r,rt<<1|1);
}
push_up(rt);
}
ll query(int a,int b,int l,int r,int rt){
if(a<=l&&b>=r)return sum[rt];
push_down(rt,r-l+1);
int mid=(l+r)>>1;
ll ans=0;
if(a<=mid){
ans+=query(a,b,lson);
}
if(b>mid)ans+=query(a,b,rson);
return ans;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
while(m--){
char s[2];
int a,b;
ll c;
scanf("%s",s);
if(s[0]=='C'){
scanf("%d%d%lld",&a,&b,&c);
update(a,b,c,1,n,1);
}else{
scanf("%d%d",&a,&b);
printf("%lld\n",query(a,b,1,n,1));
}
}
return 0;
}