zoukankan      html  css  js  c++  java
  • 线段树2:区间乘法实现

    之前的线段树1相信大家都做过了。线段树1要求能够实现区间加法和区间查询,那如果再添加一项区间乘呢?


    显然,之前我们引入的懒标记是为了更快速的实现区间修改。
    但这也仅仅是对于同等级的运算
    因为不同运算等级的运算是无法累积同一懒标记的。
    应该很好理解。就好比你要把你的孩子结点进行区间乘操作,但是你只是把你存储的懒标记乘了一下,是无法达到效果的。


    考虑引入两个懒标记。
    一个负责存储区间加的懒标记,另外一个负责存储区间乘的。
    这样down操作就会变得更加繁琐,因为需要维护的东西多了一个~~ 这谁撑得住 ~~
    说说具体实现吧。
    题目由三种操作。
    区间查询,加,乘。
    我们自己需要添加一个down,懒标记下传。


    首先建树。~~ 我在这个坑里卡了整整1day ~~
    先循环输入n次,用a数组临时存储下来。
    然后build(建树)

    void build(long long k,long long l,long long r)//建树 
    {
    	tree[k].l=l,tree[k].r=r;
    	tree[k].add_=0,tree[k].mul_=1;
    	if(l==r)
    		tree[k].w=a[l];
    	else
    	{
    		int m=(l+r)/2;
    		build(k*2,l,m);
    		build(k*2+1,m+1,r);
    		tree[k].w=tree[k*2].w+tree[k*2+1].w;
    	}
    	tree[k].w%=p;
    	return;
    }
    

    建树和1差不多,不过因为这次是用a数组临时存储的,所以就把scanf改成了赋值而已。另外多了个取模的操作。一样是二分,从根节点开始,往自己的孩子递归,直到叶子结点,然后给叶子结点赋值之后,返回更新当前结点的值。

    接下来就是如何同时实现区间乘和区间加了。
    虽然有取模操作,但是加法和乘法对于取模这件事是自由的。
    但是两种不同运算等级的在线修改怎么实现呢?
    引入上面说的两个懒标记。
    然后考虑计算的优先级。
    因为就只有乘法和加法,分开讨论就行了。
    1.加法优先。
    会导致一些应该乘的部分没有完成,以及各种奇奇怪怪的问题。
    2.乘法优先。
    这样的话,修改加法懒标记时就修改,乘法的时候就顺带修改一下加法的就可以了。
    似乎2看起来明显比1要好。
    那就选择乘法优先!
    我和之前一样,结构体里存储了基本的:左端点,右端点,区间值,还加上了两个懒标记。

    struct node
    {
    	long long l,r,w;
    	long long add_,mul_;//懒标记 
    }tree[4*100000+10];
    

    接下来讲下传操作。
    每次需要更新的:
    两个儿子结点的区间值、加法懒标记和乘法懒标记,以及清空父节点的懒标记。
    因为是乘法优先,那么顺序是:区间值、乘法、加法、清空。
    看起来很繁琐实际上只要理解了顺序就行。

    void down(long long k)//双懒标记下传 
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	tree[k*2].w=(tree[k*2].w*tree[k].mul_+(m-l+1)*tree[k].add_)%p;
    	tree[k*2+1].w=(tree[k*2+1].w*tree[k].mul_+(r-m)*tree[k].add_)%p;
    	tree[k*2].mul_=(tree[k*2].mul_*tree[k].mul_)%p;
    	tree[k*2+1].mul_=(tree[k*2+1].mul_*tree[k].mul_)%p;
    	tree[k*2].add_=(tree[k*2].add_*tree[k].mul_+tree[k].add_)%p;
    	tree[k*2+1].add_=(tree[k*2+1].add_*tree[k].mul_+tree[k].add_)%p;
    	tree[k].mul_=1;
    	tree[k].add_=0;
    	return;
    }
    

    两个在线修改操作也和之前一样就行了。依然是二分

    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	if(y<l||r<x)return;
    	if(x<=l&&y>=r)
    	{
    		tree[k].w=(tree[k].w*mul)%p;
    		tree[k].mul_=(tree[k].mul_*mul)%p;
    		tree[k].add_=(tree[k].add_*mul)%p;
    		return;
    	}
    	down(k);
    	mu(k*2);
    	mu(k*2+1);
    	tree[k].w=(tree[k*2].w+tree[k*2+1].w)%p;
    	return;
    }
    void ad(long long k)
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	if(y<l||r<x)return;
    	if(x<=l&&y>=r)
    	{
    		tree[k].w=(tree[k].w+add*(r-l+1))%p;
    		tree[k].add_=(tree[k].add_+add)%p;
    		return;
    	}
    	down(k);
    	ad(k*2);
    	ad(k*2+1);
    	tree[k].w=(tree[k*2].w+tree[k*2+1].w)%p;
    	return;
    }
    

    最后是区间查询,还是二分,查到就加上返回。

    long long ask(long long k)
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	if(y<l||r<x)return 0;
    	if(x<=l&&y>=r)return tree[k].w;
    	down(k);
    	return (ask(k*2)+ask(k*2+1))%p;
    }
    

    题目门
    完整代码贴下面:

    using namespace std;
    long long n,m,x,y,mul,add;
    struct node
    {
    	long long l,r,w;
    	long long add_,mul_;//懒标记 
    }tree[4*100000+10];
    long long p,a[100010];
    void build(long long k,long long l,long long r)//建树 
    {
    	tree[k].l=l,tree[k].r=r;
    	tree[k].add_=0,tree[k].mul_=1;
    	if(l==r)
    		tree[k].w=a[l];
    	else
    	{
    		int m=(l+r)/2;
    		build(k*2,l,m);
    		build(k*2+1,m+1,r);
    		tree[k].w=tree[k*2].w+tree[k*2+1].w;
    	}
    	tree[k].w%=p;
    	return;
    }
    void down(long long k)//双懒标记下传 
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	tree[k*2].w=(tree[k*2].w*tree[k].mul_+(m-l+1)*tree[k].add_)%p;
    	tree[k*2+1].w=(tree[k*2+1].w*tree[k].mul_+(r-m)*tree[k].add_)%p;
    	tree[k*2].mul_=(tree[k*2].mul_*tree[k].mul_)%p;
    	tree[k*2+1].mul_=(tree[k*2+1].mul_*tree[k].mul_)%p;
    	tree[k*2].add_=(tree[k*2].add_*tree[k].mul_+tree[k].add_)%p;
    	tree[k*2+1].add_=(tree[k*2+1].add_*tree[k].mul_+tree[k].add_)%p;
    	tree[k].mul_=1;
    	tree[k].add_=0;
    	return;
    }
    void mu(long long k)
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	if(y<l||r<x)return;
    	if(x<=l&&y>=r)
    	{
    		tree[k].w=(tree[k].w*mul)%p;
    		tree[k].mul_=(tree[k].mul_*mul)%p;
    		tree[k].add_=(tree[k].add_*mul)%p;
    		return;
    	}
    	down(k);
    	mu(k*2);
    	mu(k*2+1);
    	tree[k].w=(tree[k*2].w+tree[k*2+1].w)%p;
    	return;
    }
    void ad(long long k)
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	if(y<l||r<x)return;
    	if(x<=l&&y>=r)
    	{
    		tree[k].w=(tree[k].w+add*(r-l+1))%p;
    		tree[k].add_=(tree[k].add_+add)%p;
    		return;
    	}
    	down(k);
    	ad(k*2);
    	ad(k*2+1);
    	tree[k].w=(tree[k*2].w+tree[k*2+1].w)%p;
    	return;
    }
    long long ask(long long k)
    {
    	long long l=tree[k].l,r=tree[k].r,m=l+(r-l)/2;
    	if(y<l||r<x)return 0;
    	if(x<=l&&y>=r)return tree[k].w;
    	down(k);
    	return (ask(k*2)+ask(k*2+1))%p;
    }
    int main()
    {
    	cin>>n>>m>>p;
    	for(int i=1;i<=n;i++)
    		cin>>a[i];
    	build(1,1,n);
    	while(m--)
    	{
    		int ord;
    		cin>>ord;
    		if(ord==1)
    		{
    			cin>>x>>y>>mul;
    			mu(1);
    		}
    		else if(ord==2)
    		{
    			cin>>x>>y>>add;
    			ad(1);
    		}
    		else if(ord==3)
    		{
    			cin>>x>>y;
    			cout<<ask(1)<<endl;
    		}
    	}
    	return 0;
    }
    

    ov.

  • 相关阅读:
    Spring核心概念
    动态SQL
    SQL的映射文件
    初始MyBatis
    数据库SQL调优
    使用Spring Boot+MyBatis框架做查询操作
    SSM框架整合核心内容
    JavaScript基础
    MySQL的基本操作
    Java体系结构介绍
  • 原文地址:https://www.cnblogs.com/moyujiang/p/11384812.html
Copyright © 2011-2022 走看看