题目链接:点击查看
题目大意:给出一个数列 a 和一个数列 b ,其中数列 a 初始时全部为 0 ,数列 b 初始时是一个 1 ~ n 的排列,接下来共有 m 次操作,每次操作分为两种:
- add l r :在区间 [ l , r ] 内的 a[ i ] 都加上 1
- query l r :查询区间 [ l , r ] 内的所有 之和
题目分析:如果直接维护 a[ i ] / b[ i ] 的话可能不太好维护,但是我们可以反向思考,对于数列 b 维护一个线段树,每个节点维护一个 mmin 和一个 sum,分别代表区间内的最小值以及当前区间的贡献,这个最小值代表的是:当前区间至少还需要进行多少次 add 操作才会产生贡献,因为初始时每个叶子结点的 mmin 都将其赋值为 b[ i ] ,这样每次执行 add 操作时,如果 mmin > 1 的话,那么直接让 mmin 减一即可,如果 mmin = 1 的话,说明当前区间中,存在叶子结点经过此次 add 后悔变成 0 ,也就是 a[ i ] / b[ i ] 可以多贡献 1 了,对于所有 mmin = 1 的区间,继续向下递归,直到叶子结点位置,此时将叶子结点的 mmin 重新赋值为 b[ i ] ,并且将 sum 加一就好了
因为是区间修改和区间查询,所以需要加一个 lazy 标记
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e5+100;
struct Node
{
int l,r,mmin,sum,lazy;
}tree[N<<2];
int a[N];
void pushup(int k)
{
tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}
void pushdown(int k)
{
if(tree[k].lazy)
{
int lz=tree[k].lazy;
tree[k].lazy=0;
tree[k<<1].lazy+=lz;
tree[k<<1].mmin+=lz;
tree[k<<1|1].lazy+=lz;
tree[k<<1|1].mmin+=lz;
}
}
void build(int k,int l,int r)
{
tree[k].l=l;
tree[k].r=r;
tree[k].sum=tree[k].lazy=0;
if(l==r)
{
tree[k].mmin=a[l];
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update(int k,int l,int r)
{
if(tree[k].r<l||tree[k].l>r)
return;
if(tree[k].l>=l&&tree[k].r<=r)
{
if(tree[k].mmin>1)
{
tree[k].mmin--;
tree[k].lazy--;
return;
}
else if(tree[k].l==tree[k].r)
{
tree[k].mmin=a[tree[k].l];
tree[k].sum++;
return;
}
}
pushdown(k);
update(k<<1,l,r);
update(k<<1|1,l,r);
pushup(k);
}
int query(int k,int l,int r)
{
if(tree[k].r<l||tree[k].l>r)
return 0;
if(tree[k].l>=l&&tree[k].r<=r)
return tree[k].sum;
pushdown(k);
return query(k<<1,l,r)+query(k<<1|1,l,r);
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1;i<=n;i++)
scanf("%d",a+i);
build(1,1,n);
while(m--)
{
char s[10];
int l,r;
scanf("%s%d%d",s,&l,&r);
if(s[0]=='a')
update(1,l,r);
else
printf("%d\n",query(1,l,r));
}
}
return 0;
}