【线段树】CodeForces - 438D The Child and Sequence (区间取模)
题目大意:
有三种操作:1.进行区间求和 2.进行区间取模 3.进行单点更新
解题分析:
主要是考虑区间取模操作如何处理,不可能每次取模都对所有的数暴力取模一次,有一种很常见的剪枝就是,维护区间最大值,只有当最大值大于模数的时候,才向下继续进行取模操作,否则终止。因为有一个很显然的结论,对于每个能够取模的数,取模之后,它必然会变成小于等于它的$\frac {1}{2}$的数。所以每个数最多会被模$log$次,所以区间取模的复杂度为$O(nlogn)$。
#include <bits/stdc++.h> using namespace std; template<typename T> inline void read(T&x){ x=0;int f=1;char ch=getchar(); while(ch<'0' ||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } x*=f; } #define Lson rt<<1,l,mid #define Rson rt<<1|1,mid+1,r #define ll long long #define REP(i,s,t) for(int i=s;i<=t;i++) const int N = 1e5+5; ll mx[N<<2],sum[N<<2],a[N]; int n,m; inline void Pushup(int rt){ mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int rt,int l,int r){ if(l==r){ mx[rt]=sum[rt]=a[l]; return; } int mid=l+r>>1; build(Lson);build(Rson); Pushup(rt); } void up1(int rt,int l,int r,int loc,int val){ //单点更新 if(l==r){ mx[rt]=sum[rt]=val; return; } int mid=(l+r)>>1; if(loc<=mid)up1(Lson,loc,val); else up1(Rson,loc,val); Pushup(rt); } void up2(int rt,int l,int r,int L,int R,int md){ if(md>mx[rt])return; if(l==r){ sum[rt]%=md;mx[rt]=sum[rt]; return; } int mid=(l+r)>>1; if(L<=mid)up2(Lson,L,R,md); if(R>mid)up2(Rson,L,R,md); Pushup(rt); } ll query(int rt,int l,int r,int L,int R){ if(L<=l&&r<=R)return sum[rt]; int mid=(l+r)>>1; ll ans=0; if(L<=mid)ans+=query(Lson,L,R); if(R>mid)ans+=query(Rson,L,R); return ans; } int main(){ read(n);read(m); REP(i,1,n)read(a[i]); build(1,1,n); while(m--){ int op;read(op); if(op==1){ int l,r;read(l);read(r); printf("%lld\n",query(1,1,n,l,r)); }else if(op==2){ int l,r,md;read(l);read(r);read(md); up2(1,1,n,l,r,md); }else{ int loc,c;read(loc);read(c); up1(1,1,n,loc,c); } } }