LOJ 6029 市场--线段树区间更新之分块除法

题目链接

参考链接

一、题意:

四种区间操作:加、除(向下取整)、询问区间和、询问最小值

 

二、思路:

第一想到的是将除d变成乘1/d,但是由于不一定是整除,所以除法和加法运算顺序不同,会影响结果。比如 3和3,先除2后加结果是2,先加后除2答案是3

第二既然区间更新不好做,那可以暴力单点更新?显然这种第51号元素的做法妥妥的TLE~

所以我们要想怎么保持区间更新,然后尽量把除法变为减法

首先有个玄学玩意叫除法分块,对于n/i而言,总存在连续的区间值是相等的(更多应用链接)于是我们可以在所求区间基础上,去维护这个变化值相同(即max和min变化一样)的小区间,然后用区间减法维护(式子:a=a-(a-a/d))

简单的连续区间更新见poj1540

 

三、前置技能

我们先看一下乘法和加法共存怎么维护,由于可以用结合律拆分(a+b)*c=a*c+b*c,处理乘法时,同时将乘加两种懒惰标记乘c即可

  模板P3373

HDU 4578 加上维护p方

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define ll long long

inline int read(){
    int x=0; bool f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    if(f) return x;
    return 0-x;
}
int n,m;
ll mod;
ll a[100005],sum[400005],min1[400005],max1[400005],mul[400005],laz[400005];
void pushup(int i){sum[i]=(sum[(i<<1)]+sum[(i<<1)|1]);min1[i]=min(min1[(i<<1)],min1[(i<<1)|1]);max1[i]=max(max1[(i<<1)],max1[(i<<1)|1]);}
void pushdown(int i,int s,int t){
    int l=(i<<1),r=(i<<1)|1,mid=(s+t)>>1;
    if(mul[i]!=1){
    mul[l]=mul[i]*mul[l]%mod;mul[r]=mul[i]*mul[r]%mod;
    laz[l]=mul[i]*laz[l]%mod;laz[r]=laz[r]*mul[i]%mod;
    sum[l]=sum[l]*mul[i]%mod;sum[r]=sum[r]*mul[i]%mod;
    mul[i]=1;
    }
    if(laz[i]){
    sum[l]+=laz[i]*(mid-s+1);
    sum[l]%=mod;
    sum[r]+=laz[i]*(t-mid);
    sum[r]%=mod;
//    min1[l]+=laz[i];
//    min1[r]+=laz[i];
//    
//    max1[l]+=laz[i];
//    max1[r]+=laz[i];
    laz[l]+=laz[i];
    laz[l]%=mod;
    laz[r]+=laz[i];
    laz[r]%=mod;
    laz[i]=0;
    }
}
void build(int l,int r,int i){
     mul[i]=1;
    if(l==r){sum[i]=min1[i]=max1[i]=a[l];return;}
    int mid=(l+r)>>1;
    build(l,mid,i<<1);build(mid+1,r,(i<<1)|1);pushup(i);
}
void cheng(int l,int r,int s,int t,int i,ll z){
    int mid=(s+t)>>1;
    if(l<=s&&t<=r){
    	//简单的乘加维护 
    	mul[i]=(mul[i]*z)%mod;laz[i]=(laz[i]*z)%mod;
        sum[i]=sum[i]*z%mod;return;
    }
    pushdown(i,s,t);
    if(mid>=l)cheng(l,r,s,mid,(i<<1),z);
    if(mid+1<=r)cheng(l,r,mid+1,t,(i<<1)|1,z);
    pushup(i);
}
void add(int l,int r,int s,int t,int i,ll z){
    int mid=(s+t)>>1;
    if(l<=s&&t<=r){
        sum[i]+=z*(t-s+1)%mod;laz[i]=(laz[i]+z)%mod;
        max1[i]+=z,max1[i]%=mod,min1[i]+=z,min1[i]%=mod;
        return;
    }
    pushdown(i,s,t);
    if(mid>=l)add(l,r,s,mid,(i<<1),z);
    if(mid+1<=r)add(l,r,mid+1,t,(i<<1)|1,z);
    pushup(i);
}
ll query(int l,int r,int s,int t,int i){
    int mid=(s+t)>>1;ll tot=0;
    if(l<=s&&t<=r){return sum[i];}
    pushdown(i,s,t);
    if(mid>=l)tot+=query(l,r,s,mid,(i<<1));
    tot%=mod;
    if(mid+1<=r)tot+=query(l,r,mid+1,t,(i<<1)|1);
    pushup(i);
    return tot%mod;
}
ll querymin(int l,int r,int s,int t,int i){
	  int mid=(s+t)>>1;
	  ll tot=(ll)1e12;
    if(l<=s&&t<=r){return min1[i];}
    pushdown(i,s,t);
    if(mid>=l)tot=min(tot,querymin(l,r,s,mid,(i<<1)));
    
    if(mid+1<=r)tot=min(tot,querymin(l,r,mid+1,t,(i<<1)|1));
    pushup(i);
    return tot;
}
int main()
{
    int i,j,x,y,q;
    ll z;
    cin>>n>>m>>mod;//n=read();m=read();
    for(i=1;i<=n;i++)a[i]=read();
    build(1,n,1);
    for(i=1;i<=m;i++){
        q=read();
        if(q==1){
            x=read();y=read();scanf("%lld",&z);
            
            cheng(x,y,1,n,1,z);
        }
        else if(q==2){
            x=read();y=read();scanf("%lld",&z);
            
            add(x,y,1,n,1,z);
        }
        else if(q==3){
            x=read();y=read();
            
            printf("%lld\n",query(x,y,1,n,1));
        }
//        else if(q==3){
//            x=read();y=read();
//            ++x,++y;
//            printf("%lld\n",querymin(x,y,1,n,1));
//        }
    }
       return 0;
}

 

四、本题代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define ll long long
inline int read(){
    int x=0; bool f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    if(f) return x;
    return 0-x;
}
int n,m;
ll mod;
ll a[100005],sum[400005],min1[400005],max1[400005],mul[400005],laz[400005];
void pushup(int i){sum[i]=(sum[(i<<1)]+sum[(i<<1)|1]);min1[i]=min(min1[(i<<1)],min1[(i<<1)|1]);max1[i]=max(max1[(i<<1)],max1[(i<<1)|1]);}
void pushdown(int i,int s,int t){
    int l=(i<<1),r=(i<<1)|1,mid=(s+t)>>1;
//    if(mul[i]!=1){
//    mul[l]*=mul[i];mul[r]*=mul[i];
//    laz[l]*=mul[i];laz[r]*=mul[i];
//    sum[l]/=mul[i];sum[r]/=mul[i];
//    mul[i]=1;
//    }
    if(laz[i]){
    sum[l]+=laz[i]*(mid-s+1);
    sum[r]+=laz[i]*(t-mid);
    min1[l]+=laz[i];
    min1[r]+=laz[i];
    max1[l]+=laz[i];
    max1[r]+=laz[i];
    laz[l]+=laz[i];
    laz[r]+=laz[i];
    laz[i]=0;
    }
}
void build(int s,int t,int i){
//    mul[i]=1;
    if(s==t){sum[i]=min1[i]=max1[i]=a[s];return;}
    int mid=(s+t)>>1;
    build(s,mid,i<<1);build(mid+1,t,(i<<1)|1);pushup(i);
}
void chu(int l,int r,int s,int t,int i,ll z){
    int mid=(s+t)>>1;
    if(l<=s&&t<=r){
    	//分块除法,不能用简单的乘加维护 
    	ll c1=max1[i]-(long long)floor((double)max1[i]/z);
		ll c2=min1[i]-(long long)floor((double)min1[i]/z);
        if(c1==c2){

            laz[i]-=c1, sum[i]-=(t-s+1)*c1, max1[i]-=c1, min1[i]-=c1;
            return;
        }
//        mul[i]*=z;laz[i]/=z;
//        sum[i]/=z;return;
    }
    pushdown(i,s,t);
    if(mid>=l)chu(l,r,s,mid,(i<<1),z);
    if(mid+1<=r)chu(l,r,mid+1,t,(i<<1)|1,z);
    pushup(i);
}
void add(int l,int r,int s,int t,int i,ll z){
    int mid=(s+t)>>1;
    if(l<=s&&t<=r){
        sum[i]+=z*(t-s+1);laz[i]+=z;
        max1[i]+=z,min1[i]+=z;
        return;
    }
    pushdown(i,s,t);
    if(mid>=l)add(l,r,s,mid,(i<<1),z);
    if(mid+1<=r)add(l,r,mid+1,t,(i<<1)|1,z);
    pushup(i);
}
ll query(int l,int r,int s,int t,int i){
    int mid=(s+t)>>1;ll tot=0;
    if(l<=s&&t<=r){return sum[i];}
    pushdown(i,s,t);
    if(mid>=l)tot+=query(l,r,s,mid,(i<<1));
    
    if(mid+1<=r)tot+=query(l,r,mid+1,t,(i<<1)|1);
    pushup(i);
    return tot;
}
ll querymin(int l,int r,int s,int t,int i){
	  int mid=(s+t)>>1;
	  ll tot=(ll)1e12;
    if(l<=s&&t<=r){return min1[i];}
    pushdown(i,s,t);
    if(mid>=l)tot=min(tot,querymin(l,r,s,mid,(i<<1)));
    
    if(mid+1<=r)tot=min(tot,querymin(l,r,mid+1,t,(i<<1)|1));
    pushup(i);
    return tot;
}
int main()
{
    int i,j,x,y,q;
    ll z;
    cin>>n>>m;//n=read();m=read();
    for(i=1;i<=n;i++)a[i]=read();
    build(1,n,1);
    for(i=1;i<=m;i++){
        q=read();
        if(q==2){
            x=read();y=read();scanf("%lld",&z);
            ++x,++y;
            chu(x,y,1,n,1,z);
        }
        else if(q==1){
            x=read();y=read();scanf("%lld",&z);
            ++x,++y;
            add(x,y,1,n,1,z);
        }
        else if(q==4){
            x=read();y=read();
            ++x,++y;
            printf("%lld\n",query(x,y,1,n,1));
        }
        else if(q==3){
            x=read();y=read();
            ++x,++y;
            printf("%lld\n",querymin(x,y,1,n,1));
        }
    }
       return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值