洛谷2023维护序列(线段树区间乘+区间加)

题目描述

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

输入输出格式

输入格式:

第一行两个整数N和P(1≤P≤1000000000)。第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。第三行有一个整数M,表示操作总数。从第四行开始每行描述一个操作,输入的操作有以下三种形式: 操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c (1≤t≤g≤N,0≤c≤1000000000)。 操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。 操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式:

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

输入输出样例

输入样例#1:
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
输出样例#1:
2
35
8

说明

【样例说明】

初始时数列为(1,2,3,4,5,6,7)。
经过第1次操作后,数列为(1,10,15,20,25,6,7)。
对第2次操作,和为10+15+20=45,模43的结果是2。
经过第3次操作后,数列为(1,10,24,29,34,15,16}
对第4次操作,和为1+10+24=35,模43的结果是35。
对第5次操作,和为29+34+15+16=94,模43的结果是8。



测试数据规模如下表所示

数据编号 1 2 3 4 5 6 7 8 9 10
N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

Source: Ahoi 2009


思路:线段树,两个lazy。加和乘有通性。加:addv直接+ad;      乘:mulv直接*mu,add要变成addv*mu+ad;

tree[node].mulv&addv是用来下传的,因为在该区间时已经重新算过sumv。一开始还担心乘和加的先后顺序,后来发现这样写是可以的,因为先乘(加)后sumv马上就更改,其祖先的sumv也马上更改(pushup),pushdown将结果传给左右子区间。


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn=1000006;
struct data{
    ll sumv,addv,mulv;
}tree[maxn];
ll mod,c;
int n,m,k,t,g;
inline ll get(){
    char c;while(!isdigit(c=getchar()));
    ll v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
    return v;
}
inline void build(int node,int l,int r){
	tree[node].mulv=1;tree[node].addv=0;
    if(l==r)tree[node].sumv=get();
    else{
		int mid=(l+r)>>1;
		build(node<<1,l,mid);
		build((node<<1)+1,mid+1,r);
		tree[node].sumv=tree[node<<1].sumv+tree[(node<<1)+1].sumv;
	}
}
inline void pushdown(int node,int l,int r){
	int lson=node<<1,rson=(node<<1)+1;
	int rnum=(r-l+1)>>1;int lnum=r-l+1-rnum;
	tree[lson].sumv=(tree[lson].sumv*tree[node].mulv+lnum*tree[node].addv)%mod;
	tree[rson].sumv=(tree[rson].sumv*tree[node].mulv+rnum*tree[node].addv)%mod;
	
    tree[lson].mulv=(tree[lson].mulv*tree[node].mulv)%mod;
    tree[rson].mulv=(tree[rson].mulv*tree[node].mulv)%mod;
    
    tree[lson].addv=(tree[lson].addv*tree[node].mulv+tree[node].addv)%mod;
    tree[rson].addv=(tree[rson].addv*tree[node].mulv+tree[node].addv)%mod;
    
    tree[node].mulv=1;tree[node].addv=0;
}
inline void updata(int node,int l,int r,ll mu,ll ad){
    if(t<=l && g>=r){
	    tree[node].sumv=(tree[node].sumv*mu+(r-l+1)*ad)%mod;
	    tree[node].mulv=(tree[node].mulv*mu)%mod;
	    tree[node].addv=(tree[node].addv*mu+ad)%mod;
    }
    else{
	    pushdown(node,l,r);
	    int mid=(l+r)>>1;
	    if(t<=mid)updata(node<<1,l,mid,mu,ad);
	    if(g>mid)updata((node<<1)+1,mid+1,r,mu,ad);
	    tree[node].sumv=(tree[node<<1].sumv+tree[(node<<1)+1].sumv)%mod;
	}
}
inline ll getsum(int node,int l,int r){
    if(t<=l && g>=r){
		return tree[node].sumv%mod;
	}
    else{
		ll ans=0;
		pushdown(node,l,r);
		int mid=(l+r)>>1;
	    if(t<=mid)ans=(ans+getsum(node<<1,l,mid))%mod;
	    if(g>mid)ans=(ans+getsum((node<<1)+1,mid+1,r))%mod;
	    tree[node].sumv=(tree[node<<1].sumv+tree[(node<<1)+1].sumv)%mod;
		return ans%mod;
	}
}
int main(){
    n=get();mod=get();
    build(1,1,n);
    m=get();
    for(int i=1;i<=m;++i){
	    int qes=get();
	    if(qes==1){
		    t=get();g=get();c=get();updata(1,1,n,c,0);
		}
		if(qes==2){
		    t=get();g=get();c=get();updata(1,1,n,1,c);
		}
		if(qes==3){
		    t=get();g=get();
		    printf("%lld\n",getsum(1,1,n));
		}
	}
	return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值