• P3373 【模板】线段树 2


    喵喵喵的题目传送门

    这题倒是花了不少时间,错的也不少。。主要就是不能函数之间用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
    ",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("
    ");
    	}
    	return 0;
    }
    

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

  • 相关阅读:
    cocos2dx3.0戳青蛙游戏(打地鼠)
    深入理解Tomcat系列之五:Context容器和Wrapper容器
    linux下拷贝隐藏文件
    8.8.1 运行计划
    UVALive
    堆排序实现
    C语言中的signal函数
    uboot和内核分区的改动
    Android缩放动画
    .Net 自定义应用程序配置
  • 原文地址:https://www.cnblogs.com/hahaha2124652975/p/11206540.html
Copyright © 2020-2023  润新知