一、题意:
四种区间操作:加、除(向下取整)、询问区间和、询问最小值
二、思路:
第一想到的是将除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即可
#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;
}