洛谷 2023 [AHOI2009]维护序列

洛谷 2023 [AHOI2009]维护序列

 洛谷原题传送门

    这个题也是一道经典的线段树模版(其实洛谷的模版二改一下输入顺序就能AC),其中包括区间乘法修改、区间加法修改、区间查询三个操作。

 线段树的基本操作就不再赘述了,建树,查询,修改,都比较简单,我们可以为两种操作的懒惰标记申请两个变量来记录,这道题的主要难点是down操作和两种修改的优先级问题。

 这两个问题其实就是一回事:首先当我们下推标记时,如果该点加法标记不为0且乘法标记不为1(乘法标记初始化为1),那我们应该先推哪个标记呢?

 实际上我们应该先推位置靠前(也就是比较早输入)的运算,可是运用懒惰标记后,我们是难以判断那个运算输入的更早。

 有一种解决方法就是在每次乘法修改时,将对应位置的加法懒惰标记乘以修改值。这样我们保证了每次下推标记时必然是乘法优先。

if(tree[u].l>=x&&tree[u].r<=y)
    {
        tree[u].w*=c;
        tree[u].w%=mod;
        tree[u].mu=(tree[u].mu*c)%mod;
        tree[u].ad=(tree[u].ad*c)%mod;
        return;
    }

这里的模运算的性质:

((a*b) % p * c)% p = (a * (b*c) % p) % p 

(a * b) % p = (b * a) % p 

我们可以随时对程序里的标记和值做模运算。

而下推标记时,我们只要保证乘法优先,就可以轻松打出代码:

void down(int u)
{
    tree[u<<1].w=(tree[u<<1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1].r-tree[u<<1].l+1)%mod)%mod;
    tree[u<<1|1].w=(tree[u<<1|1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1|1].r-tree[u<<1|1].l+1)%mod)%mod;
    tree[u<<1].mu=tree[u].mu*tree[u<<1].mu%mod;
    tree[u<<1|1].mu=tree[u].mu*tree[u<<1|1].mu%mod;
    tree[u<<1].ad=(tree[u].ad+tree[u<<1].ad*tree[u].mu)%mod;   //下推时加法标记也要做乘法运算
    tree[u<<1|1].ad=(tree[u].ad+tree[u<<1|1].ad*tree[u].mu)%mod;
    tree[u].ad=0;   //清空标记
    tree[u].mu=1;
}

下附完整AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100010;
typedef long long ll;

struct segtree{
    int l;
    int r;
    ll w;
    ll ad;
    ll mu;
}tree[MAXN<<2];

int n,m,q,x,y;

ll mod,c,ans;

ll read()
{
    ll x=0;
    int k=1;
    char c=getchar();
    while(c>'9'||c<'0')
    {
        if(c=='-') k=-1;
        c=getchar();
    }
    while(c<='9'&&c>='0')
        x=x*10+c-'0',
        c=getchar();
    return k*x;
}

void build(int u,int l,int r)
{
    tree[u].l=l;
    tree[u].r=r;
    tree[u].ad=0;
    tree[u].mu=1;
    if(l==r)
    {
        tree[u].w=read()%mod;
        return;
    }
    int mid=(l+r)>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    tree[u].w=(tree[u<<1].w+tree[u<<1|1].w)%mod;
    return;
}

void down(int u)
{
    tree[u<<1].w=(tree[u<<1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1].r-tree[u<<1].l+1)%mod)%mod;
    tree[u<<1|1].w=(tree[u<<1|1].w*tree[u].mu%mod+tree[u].ad*(tree[u<<1|1].r-tree[u<<1|1].l+1)%mod)%mod;
    tree[u<<1].mu=tree[u].mu*tree[u<<1].mu%mod;
    tree[u<<1|1].mu=tree[u].mu*tree[u<<1|1].mu%mod;
    tree[u<<1].ad=(tree[u].ad+tree[u<<1].ad*tree[u].mu)%mod;
    tree[u<<1|1].ad=(tree[u].ad+tree[u<<1|1].ad*tree[u].mu)%mod;
    tree[u].ad=0;
    tree[u].mu=1;
}

void query(int u)
{
    if(tree[u].l>=x&&tree[u].r<=y)
    {
        ans=(ans+tree[u].w)%mod;
        return;
    }
    down(u);
    int mid=(tree[u].l+tree[u].r)>>1;
    if(x<=mid) query(u<<1);
    if(y>mid) query(u<<1|1);
    return; 
}

void mul(int u)
{
    if(tree[u].l>=x&&tree[u].r<=y)
    {
        tree[u].w*=c;
        tree[u].w%=mod;
        tree[u].mu=(tree[u].mu*c)%mod;
        tree[u].ad=(tree[u].ad*c)%mod;
        return;
    }
    down(u);
    int mid=(tree[u].l+tree[u].r)>>1;
    if(x<=mid) mul(u<<1);
    if(y>mid) mul(u<<1|1);
    tree[u].w=(tree[u<<1].w+tree[u<<1|1].w)%mod;
    return;
}

void add(int u)
{
    if(tree[u].l>=x&&tree[u].r<=y)
    {
        tree[u].ad=(tree[u].ad+c)%mod;
        tree[u].w+=(tree[u].r-tree[u].l+1)*c;
        tree[u].w%=mod;
        return;
    }
    down(u);
    int mid=(tree[u].l+tree[u].r)>>1;
    if(x<=mid) add(u<<1);
    if(y>mid) add(u<<1|1);
    tree[u].w=(tree[u<<1].w+tree[u<<1|1].w)%mod;
    return;
}

int main()
{
    n=read();
    mod=read();
    build(1,1,n);
    m=read();
    for(int i=1;i<=m;++i)
    {
        q=read();
        x=read();
        y=read();
        if(q==1)
        {
            c=read();
            mul(1);
        }
        if(q==2)
        {
            c=read();
            add(1);
        }
        if(q==3)
        {
            ans=0;
            query(1);
            printf("%lld\n",ans%mod);
        } 
    }
   return 0; }

 

posted @ 2018-07-09 16:21  Christopher_Yan  阅读(256)  评论(0编辑  收藏  举报