数学+线段树懒标记的运用 [AHOI2009]维护序列(洛谷 P2023)

[AHOI2009]维护序列

题目描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为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)。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。


看题面意思就不要多想是一道线段树的题目,但是对一个还弄不懂lazy标记到底是怎么工作的人来说,这道题确实不简单;我刚开始想的是维护每个区间和,然后分开进行加和乘法操作,这是没有错,但是这道题考的就是lazy标记了,加法的lazy就不用多说了吧,关键是乘法,我刚开始想的是加法和乘法分开计算,这样竟然TM过了样例,还全过我想的全部样例,但是交上去就是0分,这让我一度怀疑是输入输出出错了;就这样调了一下午,后来认真看了别人题解,发现这种想法根本就是错的,lazy标记往下传时,可能我加法还没传完,下一个乘法操作就来了,所以我们加法和乘法要一起往下传;那我们明显可以想到乘法优先的传法,即

tree[k<<1].w=(tree[k<<1].w*tree[k].mf+tree[k].af*(tree[k<<1].r-tree[k<<1].l+1))%p;
tree[k<<1|1].w=(tree[k<<1|1].w*tree[k].mf+tree[k].af*(tree[k<<1|1].r-tree[k<<1|1].l+1))%p;

具体操作还是看代码理解,反正我看了很久;
错误代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a[200100];
LL n,p;
LL s,x,y,z;
LL sum;
struct Node{
	int r,l;
	LL w,mf,af;
}tree[800000];
inline void build(int k,int ll,int rr){
	tree[k].l=ll,tree[k].r=rr,tree[k].af=0,tree[k].mf=1;
	if(ll==rr){
		tree[k].w=a[ll];
		return;
	}
	int m=(ll+rr)>>1;
	build(k<<1,ll,m);
	build(k<<1|1,m+1,rr);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void addpd(int k){
	if(tree[k].af){
		tree[k<<1].af=(tree[k].af+tree[k<<1].af)%p;
		tree[k<<1|1].af=(tree[k].af+tree[k<<1|1].af)%p;
		tree[k<<1].w=(tree[k<<1].w+tree[k].af*(tree[k<<1].r-tree[k<<1].l+1))%p;
		tree[k<<1|1].w=(tree[k<<1|1].w+tree[k].af*(tree[k<<1|1].r-tree[k<<1|1].l+1))%p;
		tree[k].af=0;
	}
}
inline void mulpd(int k){
	if(tree[k].mf>1){
		tree[k<<1].mf=(tree[k].mf*tree[k<<1].mf)%p;
		tree[k<<1|1].mf=(tree[k].mf*tree[k<<1|1].mf)%p;
		tree[k<<1].w=(tree[k<<1].w*tree[k].mf)%p;
		tree[k<<1|1].w=(tree[k<<1|1].w*tree[k].mf)%p;
		tree[k].mf=1;
	}
}
inline void multi(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		tree[k].w=(tree[k].w*z)%p;
		tree[k].mf=(tree[k].mf*z)%p;
		return;
	}
	mulpd(k),addpd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) multi(k<<1);
	if(y>m) multi(k<<1|1);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void add(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		tree[k].w=(tree[k].w+(tree[k].r-tree[k].l+1)*z)%p;
		tree[k].af=(tree[k].af+z)%p;
		return;
	}
	mulpd(k),addpd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) add(k<<1);
	if(y>m) add(k<<1|1);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void query(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		sum=(sum+tree[k].w)%p;
		return;
	}
	mulpd(k),addpd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) query(k<<1);
	if(y>m) query(k<<1|1);
}
int main(){
	scanf("%lld%lld",&n,&p);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	int m;
	scanf("%d",&m);
	while(m--){
		scanf("%lld%lld%lld",&s,&x,&y);
		if(s==1){
			scanf("%lld",&z);
			multi(1);
		}
		else if(s==2){
			scanf("%lld",&z);
			add(1);
		}
		else if(s==3){
			query(1);
			printf("%lld\n",sum);
			sum=0;
		}
	}
	return 0;
}

正确代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a[200100];
LL n,p;
LL s,x,y,z;
LL sum;
struct Node {
	int r,l;
	LL w,mf,af;
} tree[800000];
inline void build(int k,int ll,int rr){
	tree[k].l=ll,tree[k].r=rr,tree[k].af=0,tree[k].mf=1;
	if(ll==rr) {
		tree[k].w=a[ll];
		return;
	}
	int m=(ll+rr)>>1;
	build(k<<1,ll,m);
	build(k<<1|1,m+1,rr);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void pd(int k){
	if(tree[k].af==0&&tree[k].mf==1) return;
	tree[k<<1].w=(tree[k<<1].w*tree[k].mf+tree[k].af*(tree[k<<1].r-tree[k<<1].l+1))%p;
	tree[k<<1|1].w=(tree[k<<1|1].w*tree[k].mf+tree[k].af*(tree[k<<1|1].r-tree[k<<1|1].l+1))%p;
	tree[k<<1].af=(tree[k].af+tree[k<<1].af*tree[k].mf)%p;
	tree[k<<1|1].af=(tree[k].af+tree[k<<1|1].af*tree[k].mf)%p;
	tree[k<<1].mf=(tree[k].mf*tree[k<<1].mf)%p;
	tree[k<<1|1].mf=(tree[k].mf*tree[k<<1|1].mf)%p;
	tree[k].af=0,tree[k].mf=1;
}
inline void multi(int k){
	if(tree[k].l>=x&&tree[k].r<=y){
		tree[k].w=(tree[k].w*z)%p;
		tree[k].mf=(tree[k].mf*z)%p;
		tree[k].af=(tree[k].af*z)%p;
		return;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) multi(k<<1);
	if(y>m) multi(k<<1|1);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void add(int k) {
	if(tree[k].l>=x&&tree[k].r<=y) {
		tree[k].w=(tree[k].w+(tree[k].r-tree[k].l+1)*z)%p;
		tree[k].af=(tree[k].af+z)%p;
		return;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) add(k<<1);
	if(y>m) add(k<<1|1);
	tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void query(int k) {
	if(tree[k].l>=x&&tree[k].r<=y) {
		sum=(sum+tree[k].w)%p;
		return;
	}
	pd(k);
	int m=(tree[k].l+tree[k].r)>>1;
	if(x<=m) query(k<<1);
	if(y>m) query(k<<1|1);
}
int main() {
	scanf("%lld%lld",&n,&p);
	for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
	build(1,1,n);
	int m;
	scanf("%d",&m);
	while(m--) {
		scanf("%lld%lld%lld",&s,&x,&y);
		if(s==1) {
			scanf("%lld",&z);
			multi(1);
		} else if(s==2) {
			scanf("%lld",&z);
			add(1);
		} else if(s==3) {
			query(1);
			printf("%lld\n",sum);
			sum=0;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值