[BZOJ3146][AHOI2009]维护序列seq

题目描述

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

有长为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,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

样例输入

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

样例输出

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。


测试数据规模如下表所示

#include<cstdio>
#define posl pos<<1 
#define posr pos<<1|1 
using namespace std;   
typedef long long LL;   
const int N=1e5+10;
struct node{
    LL sum, add, mul; int l, r;
    int len(){ return r-l+1; } 
}tree[N<<2];
int T, n, ord, t, g;
LL p, c;

void Pushup( int pos ){ 
    tree[pos].sum=(tree[posl].sum+tree[posr].sum)%p; 
}

void Pushdown( int pos ){ 
    tree[posl].mul=tree[posl].mul*tree[pos].mul%p; 
    tree[posr].mul=tree[posr].mul*tree[pos].mul%p; 
	
    tree[posl].sum=tree[posl].sum*tree[pos].mul%p; 
    tree[posr].sum=tree[posr].sum*tree[pos].mul%p; 
	
    tree[posl].add=( tree[posl].add*tree[pos].mul%p+tree[pos].add )%p; 
    tree[posr].add=( tree[posr].add*tree[pos].mul%p+tree[pos].add )%p; 
	
    tree[posl].sum=( tree[posl].sum+tree[pos].add*tree[posl].len()%p )%p; 
    tree[posr].sum=( tree[posr].sum+tree[pos].add*tree[posr].len()%p )%p; 
	
    tree[pos].add=0LL; tree[pos].mul=1LL; 
}

void Build( int l, int r, int pos ){ 
    tree[pos].l=l; tree[pos].r=r; 
    tree[pos].add=0LL; tree[pos].mul=1LL; 
    if( l==r ){ 
        scanf( "%lld", &tree[pos].sum ); 
        return; 
    } 
    int mid=(l+r)>>1; 
    Build( l, mid, posl );
	Build( mid+1, r, posr ); 
    Pushup( pos ); 
}

void Change( int l, int r, int pos, LL val, bool flag ){ 
    if( tree[pos].r<l || r<tree[pos].l ) return; 
    if( l<=tree[pos].l && tree[pos].r<=r ){
        if( flag ){
			tree[pos].mul=tree[pos].mul*val%p; 
			tree[pos].add=tree[pos].add*val%p; 
			tree[pos].sum=tree[pos].sum*val%p; 
        } 
        else{ 
            tree[pos].add=( tree[pos].add+val )%p; 
            tree[pos].sum=( tree[pos].sum+val*tree[pos].len() )%p; 
        }
		return;
    }
	Pushdown( pos );
	Change( l, r, posl, val, flag );
	Change( l, r, posr, val, flag );
	Pushup( pos );
}

LL Sum( int l, int r, int pos ){
    if( tree[pos].r<l || r<tree[pos].l ) return 0;
    if( l<=tree[pos].l && tree[pos].r<=r ) return tree[pos].sum;
	Pushdown( pos );
	return ( Sum( l, r, posl )+Sum( l, r, posr ) )%p;
}

int main() {   
    scanf( "%d%lld", &n, &p ); 
    Build( 1, n, 1 ); 
    for( scanf("%d",&T); T; T-- ) {
        scanf( "%d%d%d", &ord, &t, &g ); 
        if(ord!=3) { 
            scanf( "%lld", &c );
            if( ord==1 ) Change( t, g, 1, c, 1 );
            else if( ord==2 )  Change( t, g, 1, c, 0 );
        } 
        else printf( "%lld\n", Sum( t, g, 1 ) );
    } 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值