[AHOI2009]维护序列————标准的线段树问题

· · 题解

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式:
(1)把数列中的一段数全部乘一个值;
(2)把数列中的一段数全部加一个值;
(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

没有任何掩饰,看完题目,我们就知道这是一道线段树的题。

这道题要求处理区间加,乘即求和,这恰好符合线段树的维护特征。

所以,我们就可以建一颗线段树->

inline void build(int x,int l,int r)
{
    tree[x].l=l;
    tree[x].r=r;
    tree[x].flag=1;
    tree[x].tag=0;
    if(tree[x].l==tree[x].r)
    {
        tree[x].tot=a[l];
        tree[x].tot%=mods;
        return ;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    tree[x].tot=tree[x<<1].tot+tree[x<<1|1].tot;
    tree[x].tot%=mods;
    return ;
}

由于线段树要处理区间内的加法乘法问题,根据数学知识,我们可以得知乘法对加法有影响,而加法对乘法没影响。so我们用flag作为乘法标记,初始值为1;tag做加法标记,初始值为0;

特别提醒,结构体数组范围一定至少是普通数组的四倍,即

struct node
{
    int l;
    int r;
    int flag;
    int tot;
    int tag;
}
tree[4*p];
int a[p];

否则只有50分。

然后是pushdown,注意乘法标记和加法标记的关系

inline void pushdown(int x,int l,int r)
{
    int mid=(l+r)>>1;
    tree[x<<1].tot=(tree[x<<1].tot*tree[x].flag+(mid-l+1)*tree[x].tag)%mods;
    tree[x<<1|1].tot=(tree[x<<1|1].tot*tree[x].flag+(r-mid)*tree[x].tag)%mods;
    tree[x<<1].flag=(tree[x<<1].flag*tree[x].flag)%mods;
    tree[x<<1|1].flag=(tree[x<<1|1].flag*tree[x].flag)%mods;
    tree[x<<1].tag=(tree[x<<1].tag*tree[x].flag+tree[x].tag)%mods;
    tree[x<<1|1].tag=(tree[x<<1|1].tag*tree[x].flag+tree[x].tag)%mods;
    tree[x].flag=1;
    tree[x].tag=0;
}

接下来就是区间修改了。

inline void tree_plus(int x,int l,int r,int ll,int rr,int y)
{
    if(ll<=l&&rr>=r)
    {
        tree[x].tot=(tree[x].tot+y*(r-l+1))%mods;
        tree[x].tag=(tree[x].tag+y)%mods;
        return ;
    }
    if(ll>r||rr<l)
    return ;
    int mid=(r+l)>>1;
    pushdown(x,l,r);
    tree_plus(x<<1,l,mid,ll,rr,y);
    tree_plus(x<<1|1,mid+1,r,ll,rr,y);
    tree[x].tot=(tree[x<<1].tot+tree[x<<1|1].tot)%mods;
}

inline void tree_x(int x,int l,int r,int ll,int rr,int y)
{
    if(ll<=l&&rr>=r)
    {
        tree[x].tot=(tree[x].tot*y)%mods;
        tree[x].tag=(tree[x].tag*y)%mods;
        tree[x].flag=(tree[x].flag*y)%mods;
        return ;
    }
    if(ll>r||rr<l)
    return ;
    int mid=(r+l)>>1;
    pushdown(x,l,r);
    tree_x(x<<1,l,mid,ll,rr,y);
    tree_x(x<<1|1,mid+1,r,ll,rr,y);
    tree[x].tot=(tree[x<<1].tot+tree[x<<1|1].tot)%mods;
}

注意,不仅要更新节点tot的值,还要更新tag和flag. 最后是求和->

inline int get_tot(int x,int l,int r,int ll,int rr)
{
    int ts=0;
    if(l>=ll&&r<=rr)
    return tree[x].tot;
    else
    if(l>rr||r<ll)
    return 0;
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    ts+=get_tot(x<<1,l,mid,ll,rr);
    ts+=get_tot((x<<1)+1,mid+1,r,ll,rr);
    return ts%mods;
}

注意一步一%,不然很有可能丢分。

最后上完整代码:

#include<bits/stdc++.h>
#define p 234232
#define h 5001
#define int long long
#define fint register int
using namespace std;
struct node
{
    int l;
    int r;
    int flag;
    int tot;
    int tag;
}
tree[4*p];
int a[p];
int n,m;
int mods;
inline int read();
inline void build(int x,int l,int r);
inline void pushdown(int x,int l,int r);
inline void tree_plus(int x,int l,int r,int ll,int rr,int y);
inline void tree_x(int x,int l,int r,int ll,int rr,int y);
inline int get_tot(int x,int l,int r,int ll,int rr);
inline void pre();
inline void ask();
signed main()
{
    pre();
    build(1,1,n);
    ask();
    return 0;
}

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<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
} 

inline void build(int x,int l,int r)
{
    tree[x].l=l;
    tree[x].r=r;
    tree[x].flag=1;
    tree[x].tag=0;
    if(tree[x].l==tree[x].r)
    {
        tree[x].tot=a[l];
        tree[x].tot%=mods;
        return ;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    tree[x].tot=tree[x<<1].tot+tree[x<<1|1].tot;
    tree[x].tot%=mods;
    return ;
}

inline void pushdown(int x,int l,int r)
{
    int mid=(l+r)>>1;
    tree[x<<1].tot=(tree[x<<1].tot*tree[x].flag+(mid-l+1)*tree[x].tag)%mods;
    tree[x<<1|1].tot=(tree[x<<1|1].tot*tree[x].flag+(r-mid)*tree[x].tag)%mods;
    tree[x<<1].flag=(tree[x<<1].flag*tree[x].flag)%mods;
    tree[x<<1|1].flag=(tree[x<<1|1].flag*tree[x].flag)%mods;
    tree[x<<1].tag=(tree[x<<1].tag*tree[x].flag+tree[x].tag)%mods;
    tree[x<<1|1].tag=(tree[x<<1|1].tag*tree[x].flag+tree[x].tag)%mods;
    tree[x].flag=1;
    tree[x].tag=0;
}

inline void tree_plus(int x,int l,int r,int ll,int rr,int y)
{
    if(ll<=l&&rr>=r)
    {
        tree[x].tot=(tree[x].tot+y*(r-l+1))%mods;
        tree[x].tag=(tree[x].tag+y)%mods;
        return ;
    }
    if(ll>r||rr<l)
    return ;
    int mid=(r+l)>>1;
    pushdown(x,l,r);
    tree_plus(x<<1,l,mid,ll,rr,y);
    tree_plus(x<<1|1,mid+1,r,ll,rr,y);
    tree[x].tot=(tree[x<<1].tot+tree[x<<1|1].tot)%mods;
}

inline void tree_x(int x,int l,int r,int ll,int rr,int y)
{
    if(ll<=l&&rr>=r)
    {
        tree[x].tot=(tree[x].tot*y)%mods;
        tree[x].tag=(tree[x].tag*y)%mods;
        tree[x].flag=(tree[x].flag*y)%mods;
        return ;
    }
    if(ll>r||rr<l)
    return ;
    int mid=(r+l)>>1;
    pushdown(x,l,r);
    tree_x(x<<1,l,mid,ll,rr,y);
    tree_x(x<<1|1,mid+1,r,ll,rr,y);
    tree[x].tot=(tree[x<<1].tot+tree[x<<1|1].tot)%mods;
}

inline int get_tot(int x,int l,int r,int ll,int rr)
{
    int ts=0;
    if(l>=ll&&r<=rr)
    return tree[x].tot;
    else
    if(l>rr||r<ll)
    return 0;
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    ts+=get_tot(x<<1,l,mid,ll,rr);
    ts+=get_tot((x<<1)+1,mid+1,r,ll,rr);
    return ts%mods;
}

inline void pre()
{
    n=read();
    mods=read();
    for(fint i=1;i<=n;i++)
    a[i]=read();
    m=read();
    build(1,1,n);
    return ;
}

inline void ask()
{
    int num,t,g,c;
    for(fint i=1;i<=m;i++)
    {
        num=read();
        if(num==1)
        t=read(),g=read(),c=read(),tree_x(1,1,n,t,g,c);
        else
        if(num==2)
        t=read(),g=read(),c=read(),tree_plus(1,1,n,t,g,c);
        else
        if(num==3)
        t=read(),g=read(),cout<<get_tot(1,1,n,t,g)%mods<<endl;
    }
    return ;
}

最后的最后,推荐大家几道可以迅速了解线段树的题目 P3372 P3373 P1816

如果想细致的学习基础的线段树,可以点我

祝大家ac愉快