形形色色的线段树练习——codevs线段树练习1-5:线段树,树状数组及分块模板

形形色色的线段树练习

线段树练习1

http://codevs.cn/problem/1080/

//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int t,n,m,pos,a,b;
ll value;
int num[10000001];
int ord;
struct inte
{
    int l,r,sum,add;
}tree[10000001];
/*void pushdown(int now)
{
    if(tree[now].add)
    {
        tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1);
        tree[now<<1].add+=tree[now].add;
        tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1);
        tree[now<<1|1].add+=tree[now].add;
        tree[now].add=0;//防止再次pushdown时重复下放 
    }
}*/
void update(int now)
{
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if(l==r)
    {
        tree[now].sum=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void point_change(int now,int l,int r,int pos,ll value)
{
    if(l==r)
    {
        tree[now].sum+=value;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
    point_change(now<<1,l,mid,pos,value);
    else
    point_change(now<<1|1,mid+1,r,pos,value);
    update(now);
}
ll ask(int now,int l,int r)
{
    if(tree[now].l>=l&&r>=tree[now].r)
    {
        return tree[now].sum;
    }
    //pushdown(now);点修改无需标记下发 
    int mid=(tree[now].l+tree[now].r)>>1;
    ll ans=0;
    if(l<=mid)
    {
        ans+=ask(now<<1,l,r);
    }
    if(r>mid)
    {
        ans+=ask(now<<1|1,l,r);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int j=1;j<=n;j++)
    scanf("%d",&num[j]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {   
        scanf("%d",&ord);
        if(ord==1)
        {
            scanf("%d%d",&pos,&value);
            point_change(1,1,n,pos,value);
        }
        if(ord==2)
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask(1,a,b));
        }
    }
    return 0;
}



//树状数组版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,p,a,b,value;
int num[100010],c[100010];
int lowbit(int x)
{
    return x&(-x);
}
void add(int k,int value)
{
    for(int i=k;i<=n;i+=lowbit(i))
    c[i]+=value;
}
int sum(int l,int r)
{
    int ans=0;
    for(int i=r;i>0;i-=lowbit(i))
    ans+=c[i];
    for(int i=l-1;i>0;i-=lowbit(i))
    ans-=c[i];
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        add(i,num[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d%d%d",&a,&b,&value);
            for(int i=a;i<=b;i++)
            add(i,value);
        }
        if(p==2)
        {
            scanf("%d",&a);
            printf("%d\n",c[a]-sum(a-lowbit(a)+1,a-1));
        }
    }
    return 0;
}



//分块版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,a,b,p,size;
int num[100010],pos[100010],sum[100010];
void add(int x,int value)
{
    num[x]+=value;
    sum[pos[x]]+=value;
}
int count(int l,int r)
{
    int ans=0;
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    ans+=num[i];
    if(pos[l]^pos[r])
    {
        for(int i=r;pos[i]==pos[r];i--)
        ans+=num[i];
    }
    for(int i=pos[l]+1;i<pos[r];i++)//sum存储修改后区间总值 
    ans+=sum[i];
    return ans;
}
int main()
{
    scanf("%d",&n);
    size=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        pos[i]=i/size;
        sum[pos[i]]+=num[i];
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d%d",&a,&b);
            add(a,b);
        }
        if(p==2)
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",count(a,b));
        }
    }
    return 0;
} 


线段树练习2

http://codevs.cn/problem/1081/

//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int t,n,m,pos,a,b;
ll value;
int num[10000001];
int ord;
struct inte
{
    int l,r,sum,add;
}tree[10000001];
void pushdown(int now)
{
    if(tree[now].add)
    {
        tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1);
        tree[now<<1].add+=tree[now].add;
        tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1);
        tree[now<<1|1].add+=tree[now].add;
        tree[now].add=0;
    }
}
void update(int now)
{
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if(l==r)
    {
        tree[now].sum=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void point_change(int now,int l,int r,int pos,int value)
{
    if(l==r)
    {
        tree[now].sum+=value;
        return; 
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
    {
        point_change(now<<1,l,mid,pos,value);
    }
    if(pos>mid)
    {
        point_change(now<<1|1,mid+1,r,pos,value);
    }
    update(now);
}
void change(int now,int l,int r,ll value)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        tree[now].sum+=(tree[now].r-tree[now].l+1)*value;
        tree[now].add+=value;
        return;
    }
    pushdown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    change(now<<1,l,r,value);
    if(r>mid)
    change(now<<1|1,l,r,value);
    update(now);
}
ll ask(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].sum;
    }
    pushdown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    ll ans=0;
    if(l<=mid)
    {
        ans+=ask(now<<1,l,r);
    }
    if(r>mid)
    {
        ans+=ask(now<<1|1,l,r);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&num[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&ord);
        if(ord==1)
        {
            scanf("%d%d%lld",&a,&b,&value);
            change(1,a,b,value);
        }
        if(ord==2)
        {
            scanf("%d",&a);
            printf("%lld\n",ask(1,a,a));
        }
    }
    return 0;
}



//树状数组版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,p,a,b,value;
int num[100010],c[100010];
int lowbit(int x)
{
    return x&(-x);
}
void add(int k,int value)
{
    for(int i=k;i<=n;i+=lowbit(i))
    c[i]+=value;
}
int sum(int l,int r)
{
    int ans=0;
    for(int i=r;i>0;i-=lowbit(i))
    ans+=c[i];
    for(int i=l-1;i>0;i-=lowbit(i))
    ans-=c[i];
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        add(i,num[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d%d%d",&a,&b,&value);
            for(int i=a;i<=b;i++)
            add(i,value);
        }
        if(p==2)
        {
            scanf("%d",&a);
            printf("%d\n",c[a]-sum(a-lowbit(a)+1,a-1));//左子树及自身的值之和-sum(左子树第一个元素,左子树最后一个元素)
        }
    }
    return 0;
}



//分块版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,a,b,p,size,value;
int num[100010],pos[100010],ad[100010];
void add(int l,int r,int value)
{
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    num[i]+=value;
    if(pos[l]^pos[r])
    {
        for(int i=r;pos[i]==pos[r];i--)
        num[i]+=value;
    }
    for(int i=pos[l]+1;i<pos[r];i++)//add存储区间修改量 
    ad[i]+=value;
}
int main()
{
    scanf("%d",&n);
    size=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        pos[i]=i/size;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d%d%d",&a,&b,&value);
            add(a,b,value);
        }
        if(p==2)
        {
            scanf("%d",&a);
            printf("%d\n",num[a]+ad[pos[a]]);
        }
    }
    return 0;
} 


线段树练习3

http://codevs.cn/problem/1082/

//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int t,n,m,pos,a,b;
ll value;
int num[10000001];
int ord;
struct inte
{
    int l,r;
    ll sum,add;//注意注意 
}tree[10000001];
void pushdown(int now)
{
    if(tree[now].add)
    {
        tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1);
        tree[now<<1].add+=tree[now].add;
        tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1);
        tree[now<<1|1].add+=tree[now].add;
        tree[now].add=0;
    }
}
void update(int now)
{
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if(l==r)
    {
        tree[now].sum=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void point_change(int now,int l,int r,int pos,int value)
{
    if(l==r)
    {
        tree[now].sum+=value;
        return; 
    }
    int mid=(l+r)>>1;
    if(pos<=mid)
    {
        point_change(now<<1,l,mid,pos,value);
    }
    if(pos>mid)
    {
        point_change(now<<1|1,mid+1,r,pos,value);
    }
    update(now);
}
void change(int now,int l,int r,ll value)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        tree[now].sum+=(tree[now].r-tree[now].l+1)*value;
        tree[now].add+=value;
        return;
    }
    int mid=(tree[now].l+tree[now].r)>>1;
    pushdown(now);
    if(l<=mid)
    change(now<<1,l,r,value);
    if(r>mid)
    change(now<<1|1,l,r,value);
    update(now);
}
ll ask(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].sum;
    }
    pushdown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    ll ans=0;
    if(l<=mid)
    {
        ans+=ask(now<<1,l,r);
    }
    if(r>mid)
    {
        ans+=ask(now<<1|1,l,r);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&num[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&ord);
        if(ord==1)
        {
            scanf("%d%d%lld",&a,&b,&value);
            change(1,a,b,value);
        }
        if(ord==2)
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask(1,a,b));
        }
    }
    return 0;
}



//树状数组版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
ll n,m,p,a,b,value,sum1,sum2;
ll num[200010],c1[200010],c2[200010];
ll lowbit(ll x)
{
    return x&(-x);
}
void add(ll r[],ll pos,ll value)
{
    for(ll i=pos;i<=n;i+=lowbit(i))
    r[i]+=value;
}
ll sigma(ll *r,ll pos)
{
    ll ans=0;
    for(ll i=pos;i>0;i-=lowbit(i))
    ans+=r[i];
    return ans;
}
int main()
{
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);
        add(c1,i,num[i]-num[i-1]);
        add(c2,i,(i-1)*(num[i]-num[i-1]));
    }
    scanf("%lld",&m);
    for(ll i=1;i<=m;i++)
    {
        scanf("%lld",&p);
        if(p==1)
        {
            scanf("%lld%lld%lld",&a,&b,&value);
            add(c1,a,value);
            add(c2,a,(a-1)*value);
            add(c1,b+1,-value);
            add(c2,b+1,b*(-value));
        }
        if(p==2)
        {
            scanf("%lld%lld",&a,&b);
            sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1);
            sum2=b*sigma(c1,b)-sigma(c2,b);
            printf("%lld\n",sum2-sum1);
        }
    }
    return 0;
}

这里对以上内容做一点介绍:
设原数组为num[n],差分数组c1[n],则c1[i]=num[i]-num[i-1],明显地,num[i]=sigma(c1,i),如果想要修改num[i]到num[j],只需令c1[i]+=v,c1[j+1]-=v即可.
区间修改时间复杂度O(logn)
观察式子:
num[1]+num[2]+…+num[n]
= (c1[1])+(c1[1]+c1[2])+…+(c1[1]+c1[2]+…+c1[n])
= n * c1[1]+(n-1) * c1[2]+…+c1[n]
= n * (c1[1]+c1[2]+…+c1[n]) - (0 * c1[1]+1 * c1[2]+…+(n-1) * c1[n])
维护一个数组c2[n],其中c2[i] = (i-1)*c1[i],每当修改c1的时候,就同步修改一下c2
上式
=n*sigma(c1,n) - sigma(c2,n)
区间和查询,时间复杂度O(logn)
学习借鉴自http://blog.csdn.net/fsahfgsadhsakndas/article/details/52650026十分感谢.

//分块版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll; 
int n,m,a,b,p,size,value;
int num[200010],pos[200010];
ll sum[200010],ad[200010];
void add_1(int x,int value)
{
    num[x]+=value;
    sum[pos[x]]+=value;
}
void add(int l,int r,int value)
{
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    add_1(i,value);
    if(pos[l]^pos[r])
    {
        for(int i=r;pos[i]==pos[r];i--)
        add_1(i,value);
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    ad[i]+=value;
}
ll count(int l,int r)
{
    ll ans=0;
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    ans+=num[i]+ad[pos[l]];
    if(pos[l]^pos[r])
    {
        for(int i=r;pos[i]==pos[r];i--)
        ans+=num[i]+ad[pos[r]];
    }
    for(int i=pos[l]+1;i<pos[r];i++)//sum存储非完整修改此区间后区间总值,ad存储区间修改值 
    ans+=sum[i]+ad[i]*size;
    return ans;
}
int main()
{
    scanf("%d",&n);
    size=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        pos[i]=i/size;
        sum[pos[i]]+=num[i];
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&p);
        if(p==1)
        {
            scanf("%d%d%d",&a,&b,&value);
            add(a,b,value);
        }
        if(p==2)
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",count(a,b));
        }
    }
    return 0;
}


后面的好像不能用树状数组做了…..

线段树练习4

http://codevs.cn/problem/4919/

//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,a1,b1;
int num[1000010],tmp[10],tnp[10];
ll value;
char p[10000];
struct inte
{
    int l,r;
    ll add;
    ll c[10];
}tree[1000010];
void update(int now)
{
    for(int i=0;i<7;i++)
    tree[now].c[i]=tree[now<<1].c[i]+tree[now<<1|1].c[i];
}
void pushdown(int now)
{
    if(tree[now].add)
    {
        for(int i=0;i<7;i++)
        {
            tmp[(i+tree[now].add)%7]=tree[now<<1].c[i];
            tnp[(i+tree[now].add)%7]=tree[now<<1|1].c[i];
        }
        for(int i=0;i<7;i++)
        {
            tree[now<<1].c[i]=tmp[i];
            tree[now<<1|1].c[i]=tnp[i];
        }
        tree[now<<1].add+=tree[now].add;//注意是add+= 
        tree[now<<1|1].add+=tree[now].add;
        tree[now].add=0;
    }
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if(l==r)
    {
        tree[now].c[num[l]%7]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void changes(int now,int l,int r,ll value)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        int k=value%7;
        for(int i=0;i<7;i++)
        tmp[(i+k)%7]=tree[now].c[i];
        for(int i=0;i<7;i++)
        tree[now].c[i]=tmp[i];
        tree[now].add+=k;
        return;
    }
    int mid=(tree[now].l+tree[now].r)>>1;
    pushdown(now);
    if(l<=mid)
    changes(now<<1,l,r,value);
    if(r>mid)
    changes(now<<1|1,l,r,value);
    update(now);
}
ll ask(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].c[0];
    }
    pushdown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    ll ans=0;
    if(l<=mid)
    {
        ans+=ask(now<<1,l,r);
    }
    if(r>mid)
    {
        ans+=ask(now<<1|1,l,r);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&num[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",&p);
        if(p[0]=='a')
        {
            scanf("%d%d%lld",&a1,&b1,&value);
            changes(1,a1,b1,value);
        }
        if(p[0]=='c')
        {
            scanf("%d%d",&a1,&b1);
            printf("%lld\n",ask(1,a1,b1));
        }
    }
    return 0;
}


维护区间内%7余0-6的数字的个数即可

//分块版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,k,size,a,b,value;
int pos[100100],cnt[350][100100],num[100100],sum[1000]; 
char c[1000];
void add_1(int x,int value)
{
    cnt[pos[x]][num[x]]--;
    cnt[pos[x]][num[x]=(num[x]+value)%7]++;
}
void add(int l,int r,int value)
{
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    add_1(i,value);
    if(pos[l]^pos[r])
    {
        for(int i=r;pos[i]==pos[r];i--)
        add_1(i,value);
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    sum[i]+=value;
}
int count(int l,int r)
{
    int ans=0,tmp;
    for(int i=l,tmp=sum[pos[l]]%7;pos[i]==pos[l]&&i<=r;i++)
    {
        if((!num[i]&&!tmp)||num[i]+tmp==7)
        ans++;
    }
    if(pos[l]^pos[r])
    {
        for(int i=r,tmp=sum[pos[r]]%7;pos[i]==pos[r];i--)
        if((!num[i]&&!tmp)||num[i]+tmp==7)
        ans++;
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    ans+=cnt[i][(7-(sum[i]%=7))^7?7-sum[i]:0];
    return ans;
}
int main()
{
    scanf("%d",&n);
    size=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        cnt[pos[i]=i/size][num[i]%=7]++;
    }
    for(int i=n+1;i<=n+size;i++)
    pos[i]=i/size;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",&c);
        if(c[0]=='a')
        {
            scanf("%d%d%d",&a,&b,&value);
            add(a,b,value%7);
        }
        if(c[0]=='c')
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",count(a,b));
        }
    }
    return 0;
}


线段树练习4 加强版

http://codevs.cn/problem/5037/

//这个只能用分块做了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,k,size,a,b,value;
int pos[200010+450],cnt[200100/450+10][200100],num[200100],sum[200100/450+10];//数据范围 
char type;
inline int read_1()
{
    int ret=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        ret=ret*10+(ch-'0');
        ch=getchar();
    }
    return ret;
}
inline char read_2()
{
    char ch=getchar();
    while(ch<'a'||ch>'z')
    ch=getchar();
    return ch;
}
void add_1(int x,int value)
{
    cnt[pos[x]][num[x]]--;//原来的位置数量减1 
    cnt[pos[x]][num[x]=(num[x]+value)%k]++;//新位置数量加1 
}
void add(int l,int r,int value)
{
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)//从l到从左往右第一个完整的区间 
    add_1(i,value);
    if(pos[l]^pos[r])//l与r不在同一区间内 
    {
        for(int i=r;pos[i]==pos[r];i--)//从r到从右往左第一个完整的区间 
        add_1(i,value);
    }
    for(int i=pos[l]+1;i<pos[r];i++)//中间完整区间修改值之和更新 
    sum[i]+=value;
}
int count(int l,int r)
{
    int ans=0,tmp;
    for(int i=l,tmp=sum[pos[l]]%k;pos[i]==pos[l]&&i<=r;i++)//从l到从左往右第一个完整区间的结果统计 
    {
        if((!num[i]&&!tmp)||num[i]+tmp==k)//原本数字为k的倍数/改变后的数字为k的倍数 
        ans++;
    }
    if(pos[l]^pos[r])
    {
        for(int i=r,tmp=sum[pos[r]]%k;pos[i]==pos[r];i--)//从r到从右往左第一个完整区间的结果统计 
        if((!num[i]&&!tmp)||num[i]+tmp==k)
        ans++;
    }
    for(int i=pos[l]+1;i<pos[r];i++)//中间完整区间 
    ans+=cnt[i][(k-(sum[i]%=k))^k?k-sum[i]:0];//若区间修改值为k的倍数则统计区间内数字中%k0的个数,否则统计区间内数字中加上修改值为k的倍数的个数 
    return ans;
}
int main()
{
    n=read_1();//数据间存在空格采用手读 
    m=read_1();
    k=read_1();
    size=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
        cnt[pos[i]=i/size][num[i]%=k]++;
    }
    for(int i=n+1;i<=n+size;i++)
    pos[i]=i/size;
    for(int i=1;i<=m;i++)
    {
        type=read_2();
        if(type=='a')
        {
            a=read_1();
            b=read_1();
            value=read_1()%k;
            add(a,b,value);
        }
        if(type=='c')
        {
            a=read_1();
            b=read_1();
            printf("%d\n",count(a,b));
        }
    }
    return 0;
}


注释都写进程序里了.
学习借鉴自//http://blog.csdn.net/fsahfgsadhsakndas/article/details/54836031十分感谢.

线段树练习5

http://codevs.cn/problem/4927/

//线段树版本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,a,b;
int num[1000010];
ll value;
char p[1000];
struct inte
{
    int l,r;
    ll sum,add,maxn,minx,set;
    bool f;
}tree[1000010];
void pushdown(int now)
{
    if(tree[now].f)
    {
        tree[now<<1].sum=tree[now].set*(tree[now<<1].r-tree[now<<1].l+1);
        tree[now<<1|1].sum=tree[now].set*(tree[now<<1|1].r-tree[now<<1|1].l+1);
        tree[now<<1].add=0;
        tree[now<<1|1].add=0;
        tree[now<<1].maxn=tree[now].set;
        tree[now<<1|1].maxn=tree[now].set;
        tree[now<<1].minx=tree[now].set;
        tree[now<<1|1].minx=tree[now].set;
        tree[now<<1].set=tree[now].set;
        tree[now<<1|1].set=tree[now].set;
        tree[now<<1].f=1;
        tree[now<<1|1].f=1;
        tree[now].f=0;
    }
    if(tree[now].add)
    {
        tree[now<<1].sum+=tree[now].add*(tree[now<<1].r-tree[now<<1].l+1);
        tree[now<<1|1].sum+=tree[now].add*(tree[now<<1|1].r-tree[now<<1|1].l+1);
        tree[now<<1].add+=tree[now].add;
        tree[now<<1|1].add+=tree[now].add;
        tree[now<<1].maxn+=tree[now].add;
        tree[now<<1].minx+=tree[now].add;
        tree[now<<1|1].maxn+=tree[now].add;
        tree[now<<1|1].minx+=tree[now].add;
        tree[now].add=0;
    }
}
void update(int now)
{
    tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
    tree[now].maxn=max(tree[now<<1].maxn,tree[now<<1|1].maxn);
    tree[now].minx=min(tree[now<<1].minx,tree[now<<1|1].minx);
}
void build(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if(l==r)
    {
        tree[now].sum=num[l];
        tree[now].maxn=num[l];
        tree[now].minx=num[l];
        return;
    }
    int mid=(l+r)>>1;
    build(now<<1,l,mid);
    build(now<<1|1,mid+1,r);
    update(now);
}
void change(int now,int l,int r,ll value)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        tree[now].sum+=value*(tree[now].r-tree[now].l+1);
        tree[now].add+=value;
        tree[now].maxn+=value;
        tree[now].minx+=value;
        return;
    }
    pushdown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    change(now<<1,l,r,value);
    if(r>mid)
    change(now<<1|1,l,r,value);
    update(now);
}
void sets(int now,int l,int r,int value)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        tree[now].sum=value*(tree[now].r-tree[now].l+1);
        tree[now].add=0;
        tree[now].maxn=value;
        tree[now].minx=value;
        tree[now].set=value;
        tree[now].f=1;
        return;
    }
    pushdown(now);
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    sets(now<<1,l,r,value);
    if(r>mid)
    sets(now<<1|1,l,r,value);
    update(now);
}
ll  sums(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].sum;
    }
    pushdown(now);
    ll ans=0;
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    ans+=sums(now<<1,l,r);
    if(r>mid)
    ans+=sums(now<<1|1,l,r);
    return ans;
}
ll maxs(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].maxn;
    }
    pushdown(now);
    ll ans=-1000000007;
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    ans=max(ans,maxs(now<<1,l,r));
    if(r>mid)
    ans=max(ans,maxs(now<<1|1,l,r));
    return ans;
}
ll mins(int now,int l,int r)
{
    if(tree[now].l>=l&&tree[now].r<=r)
    {
        return tree[now].minx;
    }
    pushdown(now);
    ll ans=1000000007;
    int mid=(tree[now].l+tree[now].r)>>1;
    if(l<=mid)
    ans=min(ans,mins(now<<1,l,r));
    if(r>mid)
    ans=min(ans,mins(now<<1|1,l,r));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&num[i]);
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",&p);
        if(p[0]=='a')
        {
            scanf("%d%d%lld",&a,&b,&value);
            change(1,a,b,value);
        }
        if(p[0]=='s'&&p[1]=='e')
        {
            scanf("%d%d%lld",&a,&b,&value);
            sets(1,a,b,value);
        }
        if(p[0]=='s'&&p[1]=='u')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",sums(1,a,b));
        }
        if(p[0]=='m'&&p[1]=='a')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",maxs(1,a,b));
        }
        if(p[0]=='m'&&p[1]=='i')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",mins(1,a,b));
        }
    }
    return 0;
}


注意标记下发时set的优先级高于add

//分块版本RE'0
//区间编号从0开始,第一个与最后一个区间的长度不定,O(n)预处理每个区间的左右端点
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,k,size,a,b;
ll value;
int pos[100100];
ll num[100100]; 
char c[1000];
struct inte
{
    ll sum,add,sets,maxn,minx,ls,rs;//修改后区间和,增加值,赋值,最大值,最小值,左端点,右端点 
}p[100100];
void pushdown(int k)//存在set操作需标记下发 
{
    if(p[k].sets!=-1) 
    {
        for(int i=p[k].ls;i<=p[k].rs&&i<=n;i++)
        num[i]=p[k].sets;
        p[k].add=0;
        p[k].sets=-1;
    }
    if(p[k].add)
    {
        for(int i=p[k].ls;i<=p[k].rs&&i<=n;i++)
        num[i]+=p[k].add;
        p[k].add=0;
    }
}
void add(int l,int r,ll value)
{
    pushdown(pos[l]);
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    num[i]+=value;
    p[pos[l]].sum=0;//区间和与极值必须重新计算 
    p[pos[l]].maxn=-1e9+7;
    p[pos[l]].minx=1e9+7;
    for(int i=p[pos[l]].ls;i<=p[pos[l]].rs;i++)//扫描区间确定区间和与极值 
    {
        p[pos[l]].sum+=num[i];
        p[pos[l]].maxn=max(p[pos[l]].maxn,num[i]);
        p[pos[l]].minx=min(p[pos[l]].minx,num[i]);
    }
    if(pos[l]^pos[r])//若l与r位于不同区间 
    {
        pushdown(pos[r]);
        for(int i=r;pos[i]==pos[r];i--)
        num[i]+=value;
        p[pos[r]].sum=0;
        p[pos[r]].maxn=-1e9+7;
        p[pos[r]].minx=1e9+7;
        for(int i=p[pos[r]].ls;i<=p[pos[r]].rs;i++)
        {
            p[pos[r]].sum+=num[i];
            p[pos[r]].maxn=max(p[pos[r]].maxn,num[i]);
            p[pos[r]].minx=min(p[pos[r]].minx,num[i]);
        }
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    {
        if(p[i].sets!=-1)//存在set标记则将value加于set上
        p[i].sets+=value;
        else p[i].add+=value;
        p[i].sum+=value*size;
        p[i].maxn+=value;
        p[i].minx+=value;
    }
}
void sets(int l,int r,ll value)
{
    pushdown(pos[l]);
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    num[i]=value;
    p[pos[l]].sum=0;
    p[pos[l]].maxn=-1e9+7;
    p[pos[l]].minx=1e9+7;
    for(int i=p[pos[l]].ls;i<=p[pos[l]].rs;i++)
    {
        p[pos[l]].sum+=num[i];
        p[pos[l]].maxn=max(p[pos[l]].maxn,num[i]);
        p[pos[l]].minx=min(p[pos[l]].minx,num[i]);
    }
    if(pos[l]^pos[r])
    {
        pushdown(pos[r]);
        for(int i=r;pos[i]==pos[r];i--)
        num[i]=value;
        p[pos[r]].sum=0;
        p[pos[r]].maxn=-1e9+7;
        p[pos[r]].minx=1e9+7;
        for(int i=p[pos[r]].ls;i<=p[pos[r]].rs;i++)
        {
            p[pos[r]].sum+=num[i];
            p[pos[r]].maxn=max(p[pos[r]].maxn,num[i]);
            p[pos[r]].minx=min(p[pos[r]].minx,num[i]);
        }
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    {
        p[i].sum=value*size;
        p[i].sets=value;
        p[i].add=0;//将add及时清空 
        p[i].maxn=value;
        p[i].minx=value;
    }
}
ll ask_sum(int l,int r)
{
    ll ans=0;
    pushdown(pos[l]);
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    ans+=num[i]; 
    if(pos[l]^pos[r])
    {
        pushdown(pos[r]);
        for(int i=r;pos[i]==pos[r];i--)
        ans+=num[i];
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    ans+=p[i].sum;
    return ans;
}
ll ask_max(int l,int r)
{
    ll ans=-1e9+7;
    pushdown(pos[l]);
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    ans=max(ans,num[i]);    
    if(pos[l]^pos[r])
    {
        pushdown(pos[r]);
        for(int i=r;pos[i]==pos[r];i--)
        ans=max(ans,num[i]);
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    ans=max(ans,p[i].maxn);
    return ans;
}
ll ask_min(int l,int r)
{
    ll ans=1e9+7;
    pushdown(pos[l]);
    for(int i=l;pos[i]==pos[l]&&i<=r;i++)
    ans=min(ans,num[i]);    
    if(pos[l]^pos[r])
    {
        pushdown(pos[r]);
        for(int i=r;pos[i]==pos[r];i--)
        ans=min(ans,num[i]);
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    ans=min(ans,p[i].minx);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    size=sqrt(n);
    for(int i=0;i<=500;i++)//初始化 
    {
        p[i].maxn=-1e9+7;
        p[i].minx=1e9+7;
        p[i].sets=-1;//不可赋0,set存在清0操作,仍有风险错误(set可能存在赋-1操作,最好新开变量确定是否存在set操作,add同理) 
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);
        pos[i]=i/size;//从0开始 
        p[pos[i]].sum+=num[i];
        p[pos[i]].maxn=max(p[pos[i]].maxn,num[i]);
        p[pos[i]].minx=min(p[pos[i]].minx,num[i]);
    }
    p[pos[1]].ls=1;//处理每个区间左右端点 
    for(int i=2;i<=n;i++)
    {
        if(pos[i]!=pos[i-1])
        {
            p[pos[i-1]].rs=i-1;
            p[pos[i]].ls=i;
        }
    }
    p[pos[n]].rs=n;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",&c);
        if(c[1]=='d')
        {
            scanf("%d%d%lld",&a,&b,&value);
            add(a,b,value);
        }
        if(c[1]=='e')
        {
            scanf("%d%d%lld",&a,&b,&value);
            sets(a,b,value);
        }
        if(c[1]=='u')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask_sum(a,b));
        }
        if(c[1]=='a')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask_max(a,b));
        }
        if(c[1]=='i')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask_min(a,b));
        }
    }
    return 0;
}



//分块版本RE'1
//区间编号从1开始,每个区间长度为标准的√n,可直接计算区间左右端点位置
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,m,k,size,a,b;
ll value;
int pos[100100];
ll num[100100]; 
char c[1000];
struct inte
{
    ll sum,add,sets,maxn,minx,ls,rs;//修改后区间和,增加值,赋值,最大值,最小值,左端点,右端点 
}p[100100];
void pushdown(int k)
{
    if(p[k].sets!=-1)
    {
        for(int i=(k-1)*size+1;i<=min(k*size,n);i++)//可直接计算位置
        num[i]=p[k].sets;
        p[k].add=0;
        p[k].sets=-1;
    }
    if(p[k].add)
    {
        for(int i=(k-1)*size+1;i<=min(k*size,n);i++)
        num[i]+=p[k].add;
        p[k].add=0;
    }
}
void add(int l,int r,int value)
{
    int k=pos[l];
    pushdown(k);//下放l不完整区间
    for(int i=l;i<=min(k*size,r);i++)
    num[i]+=value;
    p[k].sum=0;
    p[k].maxn=-1e9+7;
    p[k].minx=1e9+7;
    for(int i=(k-1)*size+1;i<=min(k*size,n);i++)
    {
        p[k].sum+=num[i];
        p[k].maxn=max(p[k].maxn,num[i]);
        p[k].minx=min(p[k].minx,num[i]);
    }
    for(int i=pos[l]+1;i<pos[r];i++)//下放完整区间 
    {
        if(p[i].sets!=-1)
        p[i].sets+=value;
        else p[i].add+=value;
        p[i].sum+=value*size;
        p[i].maxn+=value;
        p[i].minx+=value; 
    }
    if(pos[l]==pos[r])
    return;
    k=pos[r];
    pushdown(k);//下放r不完整区间
    for(int i=(k-1)*size+1;i<=r;i++)
    num[i]+=value;
    p[k].sum=0;
    p[k].maxn=-1e9+7;
    p[k].minx=1e9+7;
    for(int i=(k-1)*size+1;i<=min(k*size,n);i++)
    {
        p[k].sum+=num[i];
        p[k].maxn=max(p[k].maxn,num[i]);
        p[k].minx=min(p[k].minx,num[i]);
    }
}
void sets(int l,int r,int value)
{
    int k=pos[l];
    pushdown(k);
    for(int i=l;i<=min(k*size,r);i++)
    num[i]=value;
    p[k].sum=0;
    p[k].maxn=-1e9+7;
    p[k].minx=1e9+7;
    for(int i=(k-1)*size+1;i<=min(k*size,n);i++)
    {
        p[k].sum+=num[i];
        p[k].maxn=max(p[k].maxn,num[i]);
        p[k].minx=min(p[k].minx,num[i]);
    }
    for(int i=pos[l]+1;i<pos[r];i++)
    {
        p[i].sets=p[i].maxn=p[i].minx=value;
        p[i].sum=value*size;
        p[i].add=0;
    }
    if(pos[l]==pos[r])
    return;
    k=pos[r];
    pushdown(k);
    for(int i=(k-1)*size+1;i<=r;i++)
    num[i]=value;
    p[k].sum=0;
    p[k].maxn=-1e9+7;
    p[k].minx=1e9+7;
    for(int i=(k-1)*size+1;i<=min(k*size,n);i++)
    {
        p[k].sum+=num[i];
        p[k].maxn=max(p[k].maxn,num[i]);
        p[k].minx=min(p[k].minx,num[i]);
    }
}
ll ask_sum(int l,int r)
{
    ll ans=0;
    int k=pos[l];
    pushdown(k);
    for(int i=l;i<=min(k*size,r);i++)
    ans+=num[i];
    for(int i=pos[l]+1;i<pos[r];i++)
    ans+=p[i].sum;
    if(pos[l]==pos[r])
    return ans;
    k=pos[r];
    pushdown(k);
    for(int i=(k-1)*size+1;i<=r;i++)
    ans+=num[i];
    return ans;
}

ll ask_max(int l,int r){
    ll ans=-1e9+7;
    int k=pos[l];
    pushdown(k);
    for(int i=l;i<=min(k*size,r);i++)
    ans=max(ans,num[i]);
    for(int i=pos[l]+1;i<pos[r];i++)
    ans=max(ans,p[i].maxn);
    if(pos[l]==pos[r])
    return ans;
    k=pos[r];
    pushdown(k);
    for(int i=(k-1)*size+1;i<=r;i++)
    ans=max(ans,num[i]);
    return ans;
}
ll ask_min(int l,int r){
    ll ans=1e9+7;
    int k=pos[l];
    pushdown(k);
    for(int i=l;i<=min(k*size,r);i++)
    ans=min(ans,num[i]);
    for(int i=pos[l]+1;i<pos[r];i++)
    ans=min(ans,p[i].minx);
    if(pos[l]==pos[r]) return ans;
    k=pos[r];
    pushdown(k);
    for(int i=(k-1)*size+1;i<=r;i++)
    ans=min(ans,num[i]);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    size=sqrt(n);
    for(int i=1;i<=500;i++)//初始化 
    {
        p[i].maxn=-1e9+7;
        p[i].minx=1e9+7;
        p[i].sets=-1;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&num[i]);
        pos[i]=(i-1)/size+1;
        p[pos[i]].sum+=num[i];
        p[pos[i]].maxn=max(p[pos[i]].maxn,num[i]);
        p[pos[i]].minx=min(p[pos[i]].minx,num[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",&c);
        if(c[1]=='d')
        {
            scanf("%d%d%lld",&a,&b,&value);
            add(a,b,value);
        }
        if(c[1]=='e')
        {
            scanf("%d%d%lld",&a,&b,&value);
            sets(a,b,value);
        }
        if(c[1]=='u')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask_sum(a,b));
        }
        if(c[1]=='a')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask_max(a,b));
        }
        if(c[1]=='i')
        {
            scanf("%d%d",&a,&b);
            printf("%lld\n",ask_min(a,b));
        }
    }
    return 0;
}


学习借鉴自//http://www.cnblogs.com/harden/p/6486594.html
第一篇博客完成,之后我会再配图的(然而并没有).

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值