[雅礼集训 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

 

posted @ 2018-12-15 23:55  siruiyang_sry  阅读(457)  评论(0编辑  收藏  举报