Codeforces 438D:The Child and Sequence

简要题意:
n个数,m种操作。
操作一:查询区间和
操作二:区间[l,r]对x取模
操作三:单点赋值
思路:

非常经典的区间取模题。

吕凯风出的div1的题。
这种题的关键在于分析复杂度,本来如果没有第三个操作,会非常放心大胆地写。
因为取模只会让值越变越小,跟区间开根基本上一个道理,该暴力的时候暴力,但是要控制暴力的次数。
单点赋值的存在动摇了我采用这种做法的决心。
然而在题解中,vfleaking分析了一波复杂度,其实这样做还是OK的。
取模的性质保证了,如果一旦被取模的数大于等于模数,在取模后会严格小于原先的一半,也就是说一个数X只会发生[logX]次实际有意义的取模。
为了保证有意义的取模,我们需要维护区间最大值,这个非常好写。
vfleaking把每个数能发生实际取模的次数称为这个数的energy,那么初始状态下的总energy是O(Nlog(ai))的。
维护区间最大值保证了每次实际暴力取模时,会使整个的energy之和至少减少1.
而单点赋值,最大能让整个区间的energy之和增加O(log(ai)).
因此整体的最大energy是O((N+M)log(ai))的。每次减小1的复杂度是均摊O(logN)(线段树深度)的。
因此整体的复杂度是O((N+M)log(ai)logN)的。
两个log在这道题依然是OK的能过的。

#include<bits/stdc++.h>
#define lson(p) p<<1
#define rson(p) p<<1|1
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int n,m,l,r,x,op;
int a[maxn];
struct node
{
    int mx;
    ll sum;
}b[4*maxn];
void pushup(int p)
{
    b[p].mx=max(b[lson(p)].mx,b[rson(p)].mx);
    b[p].sum=b[lson(p)].sum+b[rson(p)].sum;
}
void build(int p,int l,int r)
{
    if (l==r) {
        b[p].mx=b[p].sum=a[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(lson(p),l,mid);
    build(rson(p),mid+1,r);
    pushup(p);
}
void modify(int p,int l,int r,int L,int R,int v)
{
    if (L<=l&&r<=R) {
        if (b[p].mx<v) return ;
        if (l==r) {
            b[p].mx%=v; b[p].sum%=v;
            return ;
        }
        int mid=(l+r)>>1;
        modify(lson(p),l,mid,L,R,v);
        modify(rson(p),mid+1,r,L,R,v);
        pushup(p);
        return ;
    }
    int mid=(l+r)>>1;
    if (L<=mid) modify(lson(p),l,mid,L,R,v);
    if (R>mid) modify(rson(p),mid+1,r,L,R,v);
    pushup(p);
}
void setVal(int p,int l,int r,int pos,int v)
{
    if (l==r) {
        b[p].sum=b[p].mx=v;
        return ;
    }
    int mid=(l+r)>>1;
    if (pos<=mid) setVal(lson(p),l,mid,pos,v);
    else setVal(rson(p),mid+1,r,pos,v);
    pushup(p);
}
ll getSum(int p,int l,int r,int L,int R)
{
    if (L<=l&&r<=R) return b[p].sum;
    int mid=(l+r)>>1;
    ll ans=0;
    if (L<=mid) ans+=getSum(lson(p),l,mid,L,R);
    if (R>mid) ans+=getSum(rson(p),mid+1,r,L,R);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,1,n);
    while (m--) {
        scanf("%d",&op);
        if (op==1) {
            scanf("%d%d",&l,&r);
            printf("%lld
",getSum(1,1,n,l,r));
        }
        else if (op==2) {
            scanf("%d%d%d",&l,&r,&x);
            modify(1,1,n,l,r,x);
        }
        else {
            scanf("%d%d",&l,&x);
            setVal(1,1,n,l,x);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xyw5vplus1/p/13781342.html