首页 > 其他 > 详细

P3373 【模板】线段树 2

时间:2019-07-18 14:29:21      阅读:88      评论:0      收藏:0      [点我收藏+]

喵喵喵的题目传送门

这题倒是花了不少时间,错的也不少。。主要就是不能函数之间用ctrl c+ctrl v。。哪怕在像都不行。。各种错。。还得改好几次。。

那这题引入了一个乘法的概念,那该如何处理呢??由于优先级的问题,所以我们可以在push_down时,将左右子树的和数组,加法懒惰数组以及乘法懒惰数组都乘上当前节点的乘法懒惰数组的值,并附初值为1

恩基本就是这样

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000005;

int n,m,p;
ll a[N],b[N],c[N];

int get(){//快读
	char s=getchar();
	int f=1,an=0;
	while(s<‘0‘||s>‘9‘){
		if(s==‘-‘)f=-1;
		s=getchar();
	}
	while(s>=‘0‘&&s<=‘9‘){
		an=an*10+s-‘0‘;
		s=getchar();
	}
	return f*an;
}

void build(int k,int l,int r){//建树
	if(l==r){//当根节点时读入,不用存储原数组
		int u;
		u=get();
		a[k]=u%p;
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	a[k]=(a[k<<1]+a[k<<1|1])%p;
}

void push_down(int k,int l,int r){//就这里需要好好理解一下
	int ls=k<<1,rs=k<<1|1,mid=(l+r)>>1;
	a[ls]=(a[ls]*c[k])%p;
	a[rs]=(a[rs]*c[k])%p;
	b[ls]=(b[ls]*c[k])%p;
	b[rs]=(b[rs]*c[k])%p;
	c[ls]=(c[ls]*c[k])%p;
	c[rs]=(c[rs]*c[k])%p;
	a[ls]=(a[ls]+b[k]*(mid-l+1))%p;
	a[rs]=(a[rs]+b[k]*(r-mid))%p;
	b[ls]=(b[ls]+b[k])%p;
	b[rs]=(b[rs]+b[k])%p;
	c[k]=1;
	b[k]=0;
}

int getsum(int k,int l,int r,int lo,int ro){//求和
	if(l>ro||r<lo){//无交集
		return 0;
	}
	if(l>=lo&&r<=ro){//有交集
		return a[k];
	}
	if(l==r){//根节点(好像不需要这句话)
		return 0;
	}
	if(b[k]||c[k]!=1){//懒惰标记
		push_down(k,l,r);
	}
	int mid=(l+r)>>1;
	return (getsum(k<<1,l,mid,lo,ro)+getsum(k<<1|1,mid+1,r,lo,ro))%p;//返回左右子树值和
}

void add(int k,int l,int r,int lo,int ro,int q){//加法
	if(l>ro||r<lo){//无交集
		return;
	}
	if(l==r){//根节点
		a[k]=(a[k]+q)%p;
		return;
	}
	if(l>=lo&&r<=ro){//覆盖
		a[k]=(a[k]+q*(r-l+1))%p;
		b[k]=(b[k]+q)%p;
		return;
	}
	if(b[k]||c[k]!=1){//有懒惰标记
		push_down(k,l,r);
	}
	int mid=(l+r)>>1;
	add(k<<1,l,mid,lo,ro,q);//左右子树
	add(k<<1|1,mid+1,r,lo,ro,q);
	a[k]=(a[k<<1]+a[k<<1|1])%p;
}

void rid(int k,int l,int r,int lo,int ro,int q){//乘法
	if(l>ro||r<lo){//无交集
		return;
	}
	if(l==r){//根节点
		a[k]=(a[k]*q)%p;
		return;
	}
	if(l>=lo&&r<=ro){//覆盖
		a[k]=(a[k]*q)%p;
		b[k]=(b[k]*q)%p;
		c[k]=(c[k]*q)%p;
		return;
	}
	if(b[k]||c[k]!=1){//有懒惰标记
		push_down(k,l,r);
	}
	int mid=(l+r)>>1;
	rid(k<<1,l,mid,lo,ro,q);//左右子树
	rid(k<<1|1,mid+1,r,lo,ro,q);
	a[k]=(a[k<<1]+a[k<<1|1])%p;
}

int main(){
	n=get();m=get();p=get();
	for(int i=1;i<=4*n;i++){//赋初值
		c[i]=1;
	}
	build(1,1,n);//建树
	while(m--){
		int t,l,r;
		t=get();l=get();r=get();
		if(t==1){//乘法
			int k;
			k=get();
			rid(1,1,n,l,r,k);
		}
		else if(t==2){//加法
			int k;
			k=get();
			add(1,1,n,l,r,k);
		}
		else{//求和
			printf("%d\n",getsum(1,1,n,l,r)%p);
		}
		//for(int i=1;i<=2*n;i++){//调试用
		//	printf("%d(%d,%d) ",a[i],b[i],c[i]);
		//}
		//printf("\n");
	}
	return 0;
}

  哦对了结尾提一句。。一定要用longlong,and要取模。。

P3373 【模板】线段树 2

原文:https://www.cnblogs.com/hahaha2124652975/p/11206540.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!