[雅礼集训 2017 Day1]市场
试题分析
可以容易发现此题维护的是一个数据结构,支持区间加,区间除,区间查询最大值。其实就是在$\log$级复杂度内维护除法操作。
我们发现当除数很大或者此串序列大小差不多时,我们令$a_i$为原来,$b_i$为现在,则对于$[l,r]$中的任意一个数$i$,则出现$a_i-b_i$为恒值。则我们可以用线段树去维护即可。
举个例子:
当我们要在$1,2,3,4,5$中每一个数除以$1$时,我们可以发现每个数都$a_i-b_i=0$,所以做区间减法即可
当我们要在$2,3,3,3,3$中每一个数除以$2$时,$a_i-b_i=2$.所以此段区间每个数减$2$即可。
那我们则么快速寻找是否会一样,我们只要看一下当前序列最大值与最小值的变化即可,因为他们是具有代表性的。
然后就只要维护一个区间修改,最大值,最小值,区间查询的线段树即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<climits> #define int long long using namespace std; inline int read() { int f=1,ans=0;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return ans*f; } const int N=100001; int n,q; int val[N],maxn[N<<2],minn[N<<2],sum[N<<2],tag[N<<2]; void pushup(int k){ maxn[k]=max(maxn[k<<1],maxn[k<<1|1]); minn[k]=min(minn[k<<1],minn[k<<1|1]); sum[k]=sum[k<<1]+sum[k<<1|1]; } void build(int k,int l,int r){ if(l==r){maxn[k]=minn[k]=sum[k]=val[l];return;} int mid=l+r>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); pushup(k); return; } void pushdown(int k,int l,int r){ if(tag[k]==0) return; int mid=l+r>>1; sum[k<<1]+=(mid-l+1)*tag[k],sum[k<<1|1]+=(r-mid)*tag[k]; maxn[k<<1]+=tag[k],minn[k<<1]+=tag[k]; maxn[k<<1|1]+=tag[k],minn[k<<1|1]+=tag[k]; tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k]; tag[k]=0; return; } void add(int k,int l,int r,int x,int y,int w){ // cout<<"l:"<<l<<" r:"<<r<<" w:"<<w<<endl; if(x<=l&&r<=y){ maxn[k]+=w,minn[k]+=w,tag[k]+=w;sum[k]+=(r-l+1)*w; return; } pushdown(k,l,r); int mid=l+r>>1; if(x<=mid) add(k<<1,l,mid,x,y,w); if(mid<y) add(k<<1|1,mid+1,r,x,y,w); pushup(k); return; } void div(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){ int s1=maxn[k]-(int)floor((double)maxn[k]/(double)w),s2=minn[k]-(int)floor((double)minn[k]/(double)w); if(s1==s2){ tag[k]-=s1;sum[k]-=(r-l+1)*s1;maxn[k]-=s1,minn[k]-=s1; return; } } pushdown(k,l,r); int mid=l+r>>1; if(x<=mid) div(k<<1,l,mid,x,y,w); if(mid<y) div(k<<1|1,mid+1,r,x,y,w); pushup(k); return; } int query_minn(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return minn[k]; int res=LLONG_MAX,mid=l+r>>1; pushdown(k,l,r); if(x<=mid) res=min(res,query_minn(k<<1,l,mid,x,y)); if(mid<y) res=min(res,query_minn(k<<1|1,mid+1,r,x,y)); pushup(k); return res; } int query_sum(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return sum[k]; pushdown(k,l,r); int res=0,mid=l+r>>1; if(x<=mid) res+=query_sum(k<<1,l,mid,x,y); if(mid<y) res+=query_sum(k<<1|1,mid+1,r,x,y); pushup(k); return res; } void find1(int k,int l,int r){ if(l==r){cout<<maxn[k]<<" ";return;} int mid=l+r>>1; pushdown(k,l,r); find1(k<<1,l,mid),find1(k<<1|1,mid+1,r); pushup(k); return; } signed main(){ n=read(),q=read(); for(int i=1;i<=n;i++) val[i]=read(); build(1,1,n); while(q--){ int opt=read(),l=read()+1,r=read()+1; if(opt==1) { int w=read(); add(1,1,n,l,r,w); } if(opt==2){ int w=read(); div(1,1,n,l,r,w); } if(opt==3) printf("%lld\n",query_minn(1,1,n,l,r)); if(opt==4) printf("%lld\n",query_sum(1,1,n,l,r)); } }