CF438D The Child and Sequence(线段树)

题目链接:CF原网  洛谷

题目大意:维护一个长度为 $n$ 的正整数序列 $a$,支持单点修改,区间取模,区间求和。共 $m$ 个操作。

$1\le n,m\le 10^5$。其它数均为非负整数且 $\le 10^9$。


居然被这道水题卡了那么久……

主要难点就是取模操作。

我们发现一个数 $x$ 模 $i(1\le i\le x)$:

$i\le\lfloor\frac{x}{2}\rfloor$ 时:余数小于除数,所以答案小于 $\lfloor\frac{x}{2}\rfloor$。

$i>\lfloor\frac{x}{2}\rfloor$ 时:答案就是 $x-i\$,也小于 $\lfloor\frac{x}{2}\rfloor$。

所以 $x$ 每次被取模都至少减小一半。

那……就是暴力线段树!

线段树再维护一个区间最大值,当模数大于区间最大值时,直接走人。

容易发现如果没有单点修改操作,一个叶子节点最多被暴力到 $\log a_i$ 次。

有单点修改操作?那又怎样?一共只会多 $m$ 个数而已啊!

时间复杂度 $O((n+m)\log n\log a_i)$。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100010;
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    char ch=getchar();int x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int n,m,a[maxn],mx[maxn*4];
ll sum[maxn*4];    //和要开long long
inline void pushup(int o){
    sum[o]=sum[o<<1]+sum[o<<1|1];
    mx[o]=max(mx[o<<1],mx[o<<1|1]);
}
void build(int o,int l,int r){
    if(l==r) return void(sum[o]=mx[o]=a[l]);
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(o);
}
void modify(int o,int l,int r,int p,int v){
    if(l==r) return void(sum[o]=mx[o]=v);
    int mid=(l+r)>>1;
    if(mid>=p) modify(lson,p,v);
    else modify(rson,p,v);
    pushup(o);
}
ll query(int o,int l,int r,int ql,int qr){
    if(l>=ql && r<=qr) return sum[o];
    int mid=(l+r)>>1;ll ans=0;
    if(mid>=ql) ans+=query(lson,ql,qr);
    if(mid<qr) ans+=query(rson,ql,qr);
    return ans;
}    //以上基本操作,不解释。。。
void modulo(int o,int l,int r,int ql,int qr,int v){   
    if(mx[o]<v) return;    //模数大于最大值,走人
    if(l==r) return void(sum[o]=mx[o]=sum[o]%v);    //到叶子结点,暴力
    int mid=(l+r)>>1;
    if(mid>=ql) modulo(lson,ql,qr,v);    //暴力下去
    if(mid<qr) modulo(rson,ql,qr,v);
    pushup(o);
}
int main(){
    n=read();m=read();
    FOR(i,1,n) a[i]=read();
    build(1,1,n);
    FOR(i,1,m){
        int op=read(),x=read(),y=read();
        switch(op){
            case 1:printf("%lld\n",query(1,1,n,x,y));break;
            case 2:modulo(1,1,n,x,y,read());break;
            case 3:modify(1,1,n,x,y);
        }
    }
}
线段树

猜你喜欢

转载自www.cnblogs.com/1000Suns/p/10357640.html