UESTC - 1597 线段树区间乘法 加法 取模

题意: 给一个数组, 有三个操作
1. 区间乘法
2. 区间加法
3. 询问区间的和并取模。

思路:两个数组作为懒惰标记。分别是对加法和乘法的对子树传递的保存。每次乘法时, 都要把sum数组也乘起来,在向下传递时就可以先乘法再加法,注意记录下一子树sum和mul的数值的更新。

#include 
#define lk k<<1
#define rk k<<1|1
using namespace std;
const int MAXN = 100005;
const int mod = 1e9+7;
typedef long long LL;
struct T
{
    int l, r;
    LL c;
} tree[MAXN<<2];
LL add[MAXN<<2], mul[MAXN<<2];

void pushdown(int k, int d,int p)
{
    if(mul[k]==1&&add[k]==0)
        return ;
    tree[k<<1].c=tree[k<<1].c*mul[k]%p;
    tree[k<<1|1].c=tree[k<<1|1].c*mul[k]%p;
    tree[k<<1].c=(tree[k<<1].c+add[k]*(d-(d>>1)))%p;
    tree[k<<1|1].c=(tree[k<<1|1].c+add[k]*(d>>1))%p;
    mul[k<<1]=mul[k]*mul[k<<1]%p;
    mul[k<<1|1]=mul[k]*mul[k<<1|1]%p;
    add[k<<1]=(add[k<<1]*mul[k]+add[k])%p;
    add[k<<1|1]=(add[k]+add[k<<1|1]*mul[k])%p;
    add[k]=0;
    mul[k]=1;
    return ;
}
void pushup(int k,int p)
{
    tree[k].c=(tree[k<<1].c+tree[k<<1|1].c)%p;
}
void build(int l, int r, int k, int p)
{
    tree[k].l=l;
    tree[k].r=r;
    tree[k].c=0;
    mul[k]=1;
    add[k]=0;
    if(l==r)
    {
        scanf("%lld", &tree[k].c);
        return;
    }
    int mid=(tree[k].l+tree[k].r)>>1;
    build(l, mid, k<<1, p);
    build(mid+1,r,k<<1|1, p);
    pushup(k, p);
}
void update(int l, int r, int k, LL c, int op, int p)
{
    if(tree[k].l>=l&&tree[k].r<=r){
        if(op==1){
            add[k]=add[k]*c%p;
            mul[k]=mul[k]*c%p;
            tree[k].c=tree[k].c*c%p;
        }
        else{
            add[k]=(add[k]+c)%p;
            tree[k].c=(tree[k].c+(tree[k].r-tree[k].l+1)*c)%p;
        }
        return;
    }
    pushdown(k, tree[k].r-tree[k].l+1, p);
    int mid=(tree[k].l+tree[k].r)>>1;
    if(l<=mid)
        update(l,r,k<<1,c,op,p);
    if(r>mid)
        update(l,r,k<<1|1,c,op,p);
    pushup(k,p);
}
LL query(int l, int r, int k, int p)
{
    if(tree[k].l>=l&&tree[k].r<=r)
        return tree[k].c%p;
    pushdown(k, tree[k].r-tree[k].l+1, p);
    LL ans=0;
    int mid = (tree[k].l+tree[k].r)>>1;
    if(l<=mid)
        ans=(ans+query(l,r,k<<1,p))%p;
    if(r>mid)
        ans=(ans+query(l,r,k<<1|1, p))%p;
    //pushup(k, p);
    return ans%p;
}
int main()
{
    int n, m, p;
    while(~scanf("%d %d", &n,&p))
    {
        build(1, n, 1, p);
        scanf("%d", &m);
        while(m--)
        {
            int op,l,r;
            LL v;
            scanf("%d %d %d", &op, &l, &r);
            if(op==1||op==2)
            {
                scanf("%lld", &v);
                update(l, r, 1, v, op, p);
            }
            else
            {
                printf("%lld\n", query(l, r, 1, p));
            }
        }
    }
    return 0;
}

你可能感兴趣的:(线段树)