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