HDU - 6315(2018 Multi-University Training Contest 2) Naive Operations (线段树区间操作)

时间:2023-03-09 16:32:47
HDU - 6315(2018 Multi-University Training Contest 2) Naive Operations (线段树区间操作)

http://acm.hdu.edu.cn/showproblem.php?pid=6315

题意

a数组初始全为0,b数组为1-n的一个排列。q次操作,一种操作add给a[l...r]加1,另一种操作query查询Σfloor(ai/bi)(i=l...r)。

分析

真的是太naive啦,现场时没做出来。

看见区间自然想起线段树,那么这里的关键就是整除问题,只有达到一定数量才会对区间和产生影响。

反过来想,先把a[i]置为b[i],那么每次add时就是-1操作,当a[i]为0时区间和+1,再把a[i]置为b[i]。这要求我们维护区间最小值和区间和。

若当前区间最小值大于1时,那么此时对这个区间进行add操作没有实质影响,于是用lazy标记先。当区间最小值为1时,向下更新,不断向下找,找到叶结点,区间和+1,a和lazy重新设置。query时就查询区间和即可。

#include<bits/stdc++.h>
using namespace std; const int N=1e5+; int a[N<<],lazy[N<<],b[N],sum[N<<];
int n;
void PushDown(int rt){
if(lazy[rt]){
lazy[rt<<]+=lazy[rt];
lazy[rt<<|]+=lazy[rt];
a[rt<<]-=lazy[rt];
a[rt<<|]-=lazy[rt];
lazy[rt]=;
}
}
void PushUp(int rt){
a[rt]=min(a[rt<<],a[rt<<|]);
sum[rt]=sum[rt<<]+sum[rt<<|];
}
void Build(int l,int r,int rt){
lazy[rt]=sum[rt]=;
if(l==r){
a[rt]=b[l];
return;
}
int mid = (l+r)>>;
Build(l,mid,rt<<);
Build(mid+,r,rt<<|);
PushUp(rt);
}
void Update(int L,int R,int l,int r,int rt){
if(a[rt]>&&L<=l&&r<=R){
a[rt]--;
lazy[rt]++;
return;
}
if(a[rt]==&&l==r){
sum[rt]++;
lazy[rt]=;
a[rt]=b[l];
return;
}
PushDown(rt);
int mid = (r+l)>>;
if(L<=mid) Update(L,R,l,mid,rt<<);
if(mid<R) Update(L,R,mid+,r,rt<<|);
PushUp(rt);
}
int Query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return sum[rt];
}
PushDown(rt);
int mid = (r+l)>>;
int ans=;
if(L<=mid) ans+=Query(L,R,l,mid,rt<<);
if(mid<R) ans+=Query(L,R,mid+,r,rt<<|);
PushUp(rt);
return ans;
}
int main() {
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif // LOCAL
int q,x,y;
char op[];
while(scanf("%d%d",&n,&q)!=EOF) {
for(int i=; i<=n; i++)
scanf("%d",&b[i]);
Build(,n,);
for(int i=; i<q; i++) {
scanf("%s%d%d",op,&x,&y);
if(op[]=='a')
Update(x,y,,n,);
else
printf("%d\n",Query(x,y,,n,));
}
}
return ;
}