因为long long类型数据输入的错误搞了大半天,所以采用scanf和printf输入输出一定严格遵守格式控制。
先推荐一下大佬的博客链接:https://blog.csdn.net/dt2131/article/details/58689903
先说一下自己的错误吧~:输出的时候要严格按照格式,输入的时候也相同,比如,在这道题目的程序中scanf("%lld",&tree[rt])
是正确的,但是我一开始的错误输入scanf("%d",&tree[rt])在处理负数的时候会出现错误的输出,因此使用标准输入输出一定要严格遵守格式限制的规定,占位符非常重要!!!
当然,除此之外还要注意:返回的函数值一定要符合函数值的返回类型,否则也会出错。
下面是题目的AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
char str[5];
long long add[maxn<<2];//增加下推标记
long long tree[maxn<<2];
int L,R,C;
int n,m;
void pushup(int rt)
{
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
//printf("%d数字已经更新\n",tree[rt]);
return ;
}
void pushdown(int ln,int rn,int rt)
{//ln,rn分别是左子树和右子树的数字数量
if(add[rt])
{//首先将标记下推
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
//其次将左子树和右子树的结点向下更新
tree[rt<<1]+=add[rt]*ln;
tree[rt<<1|1]+=add[rt]*rn;
add[rt]=0;
}
}
void build_tree(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&tree[rt]);
//printf("%d数字已经录入\n",tree[rt]);//检验
return;
}
int m=(l+r)>>1;
build_tree(l,m,rt<<1);
build_tree(m+1,r,rt<<1|1);
pushup(rt);
}
void update(int l,int r,int rt)
{
if(L<=l&&r<=R)
{
tree[rt]+=(r-l+1)*C;
add[rt]+=C;//增加懒惰标记,表示本区间的tree值
//正确,不再下推,子区间的值使用的时候再进行修改
return;
}
int m=(l+r)>>1;
pushdown(m-l+1,r-m,rt);
if(L<=m)
update(l,m,rt<<1);
if(m<R)
update(m+1,r,rt<<1|1);
pushup(rt);
}
long long int query(int l,int r,int rt)
{
if(L<=l&&r<=R)
{
///printf("%d已经被查询到\n",tree[rt]);
return tree[rt];
}
int m=(l+r)>>1;
pushdown(m-l+1,r-m,rt);
long long ans=0;
if(L<=m)
ans+=query(l,m,rt<<1);
if(m<R)
ans+=query(m+1,r,rt<<1|1);
///printf("返回的查询值为%d\n",ans);
return ans;
}
int main()
{
memset(add,0,sizeof(add));
memset(tree,0,sizeof(tree));
scanf("%d%d",&n,&m);
build_tree(1,n,1);
for(int i=0;i<m;++i)
{
scanf("%s",str);
if(str[0]=='Q')
{//区间查询
scanf("%d%d",&L,&R);
printf("%lld\n",query(1,n,1));
}
else if(str[0]=='C')
{//区间修改
scanf("%d%d%d",&L,&R,&C);
update(1,n,1);
}
}
return 0;
}
新的知识点需要理解的是线段树的下推函数和懒惰标记的使用。
懒惰标记的作用有两个,一个是记录遍历到的位置,另一个是记录下推时要更新多大的值。
通过代码可以看出:在下一次进行区间更新或者区间查询的时候,在当前区间没有落到目标区间时,我们总要首先对当前区间进行下推操作,目的是更新子区间的值,使子区间的值保持正确后,再进行下面的利用。而一旦将懒惰标记使用后(下推操作完成,改变左子树数值和右子树数值,并且将懒惰标记的位置转移到左子树和右子树上后),懒惰标记的两个作用:遍历到的位置下移了,因此不能再使用这个懒惰标记记录当前位置作为遍历到的位置了,记录更新多大的值也转移到左子树和右子树的懒惰标记上了,因此这个懒惰标记失去了它的作用和存在的意义,于是需要add[rt]=0操作。