题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6315
题意:给出初始数组a,元素值均为0,数据数组b,两种询问方式 add,x,y 表示在区间 [x,y] 中每个元素均+1,query x y,表示计算 。
参考博客:
题解:线段树应用,我们首先我们在建立线段树之前应该思考的是线段树的节点维护一个什么值,
-
维护了b[i]的值,因为a[i]最初是0,所以在update过程中当a[i]>=b[i]时,a[i]/b[i]才>=1;
-
才会对sum有贡献,所以不妨维护b[i]的值,对每次区间更新,将区间中的b[i]减1,当b[i]减到0时,更新sum的值+1,然后b[i]更新回初始的b[i]的值
-
整个更新过程中需要维护lazy标志、sum当前子树中0的个数(即sum+1的次数)、minx当前子树中最小的b[i]值
-
lazy是a数组向下推的标志,就是加的次数,其实就是b要减的数
代码:
#include<cstdio>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=100010;
int n,m,x,y;
int num[maxn];
char op[10];
struct node{
int minx,sum,add;
}T[maxn<<2];
void build(int rt,int l,int r)
{
T[rt].add=0;
if(l==r){
T[rt].minx=num[l];
T[rt].sum=0;
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
T[rt].minx=min(T[rt<<1].minx,T[rt<<1|1].minx);
T[rt].sum=T[rt<<1].sum+T[rt<<1|1].sum;
}
void pushdown(int rt,int l,int r)
{
if(!T[rt].add) return;
int mid=(l+r)>>1;
T[rt<<1].minx+=T[rt].add;
T[rt<<1|1].minx+=T[rt].add;
T[rt<<1].add+=T[rt].add;
T[rt<<1|1].add+=T[rt].add;
T[rt].add=0;
}
/*
L R 表示查询区间
l r 表示当前区间
*/
///这里多了一个ok,不像我们之前的,不用ok,直接算,因为之前成段更新时
///可以很快从当前节点给算出来,但这里不能,只能通过叶子节点给算出来
void update(int rt,int l,int r,int L,int R,bool ok)
{
if(L>r||R<l) return;
if(L<=l&&r<=R){
if(ok){
T[rt].minx--;
T[rt].add--;
}
if(T[rt].minx>0) return;
if(l==r){
if(T[rt].minx==0){
T[rt].sum++;
T[rt].minx=num[l];
return;
}
}
ok=false;
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
update(rt<<1,l,mid,L,R,ok);
update(rt<<1|1,mid+1,r,L,R,ok);
T[rt].minx=min(T[rt<<1].minx,T[rt<<1|1].minx);
T[rt].sum=T[rt<<1].sum+T[rt<<1|1].sum;
}
int query(int rt,int l,int r,int L,int R)
{
if(L>r||R<l) return 0;
if(L<=l&&r<=R) return T[rt].sum;
int mid=(l+r)>>1;
return query(rt<<1,l,mid,L,R)+query(rt<<1|1,mid+1,r,L,R);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
scanf("%d",&num[i]);
build(1,1,n);
while(m--)
{
scanf("%s%d%d",op,&x,&y);
if(op[0]=='a') update(1,1,n,x,y,1);
else printf("%d\n",query(1,1,n,x,y));
}
}
return 0;
}