1. 程式人生 > >[雅禮集訓 2017 Day1]市場

[雅禮集訓 2017 Day1]市場

link

試題分析

可以容易發現此題維護的是一個數據結構,支援區間加,區間除,區間查詢最大值。其實就是在$\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)); } }
View Code