AcWing1277维护序列(线段树)

题目地址https://www.acwing.com/problem/content/description/1279/

题目描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。

有长为 N 的数列,不妨设为 a1,a2,,aN

有如下三种操作形式:

  1. 把数列中的一段数全部乘一个值;
  2. 把数列中的一段数全部加一个值;
  3. 询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 PP 的值。

输入格式

第一行两个整数 N 和 P

第二行含有 NN 个非负整数,从左到右依次为 a1,a2,,aN

第三行有一个整数 M,表示操作总数;

从第四行开始每行描述一个操作,输入的操作有以下三种形式:

  • 操作 11:1 t g c,表示把所有满足 tig 的 ai 改为 ai×c
  • 操作 22:2 t g c,表示把所有满足 tig 的 ai改为 ai+c
  • 操作 33:3 t g,询问所有满足 tig 的 ai的和模 P的值。

同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式

对每个操作 3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

数据范围

1N,M1e5
1tgN,
0c,ai1e9,
1P1e9

题解:这是一道典型的线段树问题。但是这里对于区间修改涉及两个操作。我们可以使用一个巧妙的办法来解决。假设一个区间的懒标记:add=a,mult=b.那么又来一个add1,则该区间的变换为sum+=(tr[u].r-tr[u].l+1)*add1,add+=add1.如果来一个mult1,则该区间的变换为sum*=mult1,mult*=mult1,add*=mult1.

剩下的就可以利用线段树直接完成了。

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+10;
#define ll long long int
ll n,m,p;
struct node{
    int l,r;
    ll sum,add,mult;
}tr[4*N];

void pushup(int u){
    tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}

void build(int u,int l,int r){
    tr[u].l=l,tr[u].r=r,tr[u].add=0,tr[u].mult=1;
    if(l==r){
        scanf("%lld",&tr[u].sum);
        tr[u].sum%=p;
        return ;
    }
    int mid=l+r>>1;
    build(u<<1,l,mid);
    build(u<<1|1,mid+1,r);
    pushup(u);
    return ;
}

void pushnow(int u,ll add,ll mult){
    tr[u].sum*=mult;
    tr[u].sum%=p;
    tr[u].sum+=add*(tr[u].r+1-tr[u].l);
    tr[u].sum%=p;
    tr[u].mult*=mult;
    tr[u].add*=mult;
    tr[u].add%=p;
    tr[u].add+=add;
    tr[u].mult%=p;
    tr[u].add%=p;
}

void pushdown(int u){
    if(tr[u].add!=0||tr[u].mult!=1){
        pushnow(u<<1,tr[u].add,tr[u].mult);
        pushnow(u<<1|1,tr[u].add,tr[u].mult);
        tr[u].add=0;
        tr[u].mult=1;
    }
}

void modify(int u,int l,int r,ll add,ll mult){
    if(tr[u].l>=l&&tr[u].r<=r){
        pushnow(u,add,mult);
        return ;
    }
    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    if(l<=mid) modify(u<<1,l,r,add,mult);
    if(r>mid) modify(u<<1|1,l,r,add,mult);
    pushup(u);
    return ;
}

ll query(int u,int l,int r){
    if(tr[u].l>=l&&tr[u].r<=r) {
        return tr[u].sum%p;
    }
    int mid=tr[u].l+tr[u].r>>1;
    ll sum=0;
    pushdown(u);
    if(l<=mid) sum=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum%p; 
}

int main(){
    cin>>n>>p;
    build(1,1,n);
    cin>>m;
    ll k,l,r,c;
    while(m--){
        scanf("%lld%lld%lld",&k,&l,&r);
        if(k==1){
            scanf("%lld",&c);
            modify(1,l,r,0,c%p);
        }
        else if(k==2){
            scanf("%lld",&c);
            modify(1,l,r,c%p,1);
        }
        else {
            cout<<query(1,l,r)<<endl;
        }
    }
    return 0;
} 

写于;202/8/29 22:08

AcWing1277维护序列(线段树)

上一篇:windows内网信息查看常用命令


下一篇:多角度让你彻底明白yield语法糖的用法和原理及在C#函数式编程中的作用