题意:
维护一个区间,满足:
1、区间求和。
2、区间取模。
3、单点修改。
分析:
看似好像是个神题,其实水的一pi。
就比如x%k,分析三种情况(此处略),发现要么x%k==x,要么x%k<=x/2。
再加上修改是单点修改,我们就可以愉快的均摊复杂度了@~@
线段树维护区间最大值和区间和。
在区间赋值时,若一个区间的最大值<模数k,直接跳过。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MAXN=100005;
int n,m,a[MAXN],maxn[MAXN<<2],ql,qr,k,loc;
LL sum[MAXN<<2];
#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
void build(int o,int l,int r){
if(l==r){
sum[o]=maxn[o]=a[l];
return;
}
build(lc,l,mid);
build(rc,mid+1,r);
sum[o]=sum[lc]+sum[rc];
maxn[o]=max(maxn[lc],maxn[rc]);
}
void modupd(int o,int l,int r){
if(maxn[o]<k) return;//标重点
if(l==r){
sum[o]=maxn[o]=maxn[o]%k;
return;
}
if(mid>=ql) modupd(lc,l,mid);
if(mid<qr) modupd(rc,mid+1,r);
sum[o]=sum[lc]+sum[rc];
maxn[o]=max(maxn[lc],maxn[rc]);
}
void upd(int o,int l,int r){
if(l==r&&l==loc){
sum[o]=maxn[o]=k;
return;
}
if(loc<=mid) upd(lc,l,mid);
else upd(rc,mid+1,r);
sum[o]=sum[lc]+sum[rc];
maxn[o]=max(maxn[lc],maxn[rc]);
}
LL query(int o,int l,int r){
if(ql<=l&&r<=qr) return sum[o];
LL ans=0;
if(mid>=ql) ans+=query(lc,l,mid);
if(mid<qr) ans+=query(rc,mid+1,r);
return ans;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
build(1,1,n);
for(int i=1;i<=m;i++){
int opt=read();
if(opt==1){
ql=read(),qr=read();
printf("%lld\n",query(1,1,n));
}
else if(opt==2){
ql=read(),qr=read(),k=read();
modupd(1,1,n);
}
else{
loc=read(),k=read();
upd(1,1,n);
}
}
return 0;
}