题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6315
题意:一行给出n,m,有两个数组,长度为n,其中a数组全为0,b数组接下来一行给你n个数
接着m行是m条命令:
add l r a数组[l,r]区间的数全都加一
query l r [l,r] 区间的数 求和, 每个数为a[i]/b[i]
一开始用树状数组做的,TLE了,比赛结束用线段数做的,不懒惰标记依然会TLE
考虑到区间每次加一后,如果求和值不一定会加一,只有当a[i]加了一个b[i]才会加1;
这样每次当a[i]加到一个b[i]时结果加一,用cnt表示结果,laz是加的数,然后cnt++后要让b[i]加上原来的一个b[i]
例如 1 5 2 4 3
对最后一个b[5]而言,a[5]要加3次值才会加一,那么加一之后让b[5]加上原来的一个b[5]变成6,依旧是让a[5]加3次结果加一
#include <bits/stdc++.h>
#define N 100005
#define lc (d<<1)
#define rc (d<<1|1)
#define m (l+r>>1)
using namespace std;
struct NODE{
int laz,cnt,min,max;
};
int b[N];
NODE node[4*N];
int n;
void pushUp(int d)
{
node[d].min=min(node[lc].min,node[rc].min);
node[d].max=max(node[lc].max,node[rc].max);
node[d].cnt=node[lc].cnt+node[rc].cnt;
}
void pushDown(int d)
{
if(node[d].laz)
{
int v=node[d].laz;
node[d].laz=0;
node[lc].laz+=v;
node[rc].laz+=v;
node[lc].max+=v;
node[rc].max+=v;//由于每次是让b[i]加上自身并不是清零,所以这里也是加v
}
}
void build(int l,int r,int d)
{
node[d].laz=0;
if(l==r)
{
node[d].cnt=node[d].max=0;
node[d].min=b[l];
return;
}
build(l,m,lc);
build(m+1,r,rc);
pushUp(d);
}
void updata(int l,int r,int L,int R,int d)
{
if(L<=l&&r<=R)
{
node[d].max++;
if(node[d].max<node[d].min)
{
node[d].laz++;
return;
}
if(l==r&&node[d].max>=node[d].min)
{
node[d].cnt++;//结果加一
node[d].min+=b[l];
return;
}
}
pushDown(d);
if(L<=m)
updata(l,m,L,R,lc);
if(R>m)
updata(m+1,r,L,R,rc);
pushUp(d);
}
int query(int l,int r,int L,int R,int d)
{
if(L<=l&&r<=R)
{
return node[d].cnt;
}
pushDown(d);
int ans=0;
if(L<=m)
ans+=query(l,m,L,R,lc);
if(R>m)
ans+=query(m+1,r,L,R,rc);
return ans;
}
int main()
{
int q;
int x,y;
while(~scanf("%d %d",&n,&q))
{
for(int i=1; i<=n; i++)
{
scanf("%d",&b[i]);
}
build(1,n,1);
char op[6];
int l,r;
while(q--)
{
scanf("%s%d%d",op,&l,&r);
if(op[0]=='a')
{
updata(1,n,l,r,1);
}
else
{
printf("%d\n",query(1,n,l,r,1));
}
}
}
return 0;
}