zoukankan      html  css  js  c++  java
  • #笔记 线段树(一)

    第二次学线段树感觉第一次和没学一样

    部分指针用法

    对于这段代码,

    struct Node{
        int a, b, c;
    }CUC[100], x;
    Node *p = CUC, *q = &x;
    

    以下代码在使用过程中是等价的:


    cout << x.a << endl;
    
    cout << q->a << endl;
    
    cout << (*q).a << endl;
    

    cout << CUC[2].b << endl;
    
    cout << *(p+2).b << endl;
    

    用指针写线段树

    相对于上一次线段树的写法,我获得了用指针写线段树的本领。

    相对于用下标来写线段树,指针的代码量、可扩展性都变好了。各种函数分别用下表和指针的不同写法在下面会陈列出来。

    初始化

    数组:

    #define N 1000000+50//数据边界
    struct tree{
    	long long l,r,sum,tag,tag_x;//l,r左右端点,sum为结点对应区间和,tag为加法标记,tag_x为乘法标记 
    }t[N];//线段树 
    long long a[N];//输入的数列(1~n) 
    long long m,n,p,k;//如题意(k是操作种类) 
    long long ls(long long rt){return rt<<1;}//左孩子 
    long long rs(long long rt){return rt<<1|1;}//右孩子 lcez_cyc
    

    指针:

    struct Node{
        int l,r;
        ll v,tag;
        Node *ls,*rs;
    
        inline void () {} 
        inline void () {} 
        inline void () {} 
        inline void () {} 
        
    }Mem[maxn << 1];
    
    Node *pool = Mem;
    

    相对来讲,指针的实现方式代码更简洁,而且在最后一层不会浪费过多的空间。线段树开空间的时候需要对极端情况进行考虑,数组往往开到maxn << 2,但是在指针这里只需要开(maxn << 1) - 1

    核心函数:(FindRange)

    数组:

    void build(long long rt,long long l,long long r) {
    	if(t[rt].l <= l && t[rt].r >= r)){ 
    		do sth.
    		return;
    	}
    	long long mid = (t[rt].l + t[rt].r) >> 1;
        if (l <= mid)
        	build(ls(rt),l,r);
    	if (r > mid)
            build(rs(rt),l,r);
        pushup(rt);
    }
    

    指针:

    void FindRange(L, R) {
        if (InRange(L, R)) {
            do sth.
            return;
        } else if (OutofRange(L, R)) return; 
        else {
            ls->FindRange(L, R);
            rs->FindRange(L, R);
        }
        pushup();
    

    可以看到,直接用指针指向左右儿子的方法不受到数组下表的限制,所以就不会有智障的四倍空间,只需要按照实际空间大小开就可以了。

    内存池

    用指针建立线段树的方法大致有两种,一种是直接在建树的时候开一个新空间Node *u = new Node,但是new这个语句常数十分大,运行起来巨慢。所以我们可以提前申请好一个Node Mem[maxn << 1 - 1]的数组,并用一个指针Node *Pool = Mem进行取出新点,从而实现代替new功能的函数:

    Node* New() { return ++Pool; }
    

    完整代码:

    数组:

    #include "bits/stdc++.h"
    #define N 1000000+50
    using namespace std;
    
    struct tree{
    	long long l,r,sum,tag,tag_x;//l,r左右端点,sum为结点对应区间和,tag为加法标记,tag_x为乘法标记 
    }t[N];//线段树 
    long long a[N];//输入的数列(1~n) 
    long long m,n,p = 9223372036854775807,k;//如题意(k是操作种类) 
    long long ls(long long rt){return rt<<1;}//左孩子 
    long long rs(long long rt){return rt<<1|1;}//右孩子 
    
    void build(long long rt,long long l,long long r){
    	t[rt].tag_x = 1; t[rt].tag = 0;//初始化
    	t[rt].l = l,t[rt].r = r;//建立一个结点,更新左右端点标记 
    	if(l == r){ //如果到了叶子结点 
    		t[rt].sum = a[l] % p; //不要忘记取模操作 
    		return;
    	}
    	long long mid = (l + r) >> 1; //中间节点 
    	build(ls(rt),l,mid);
    	build(rs(rt),mid+1,r); //如果不是叶子结点,就分别建立左右孩子 
    	t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % p; // 更新sum 
    }
    
    void push_down(long long rt){
        t[ls(rt)].tag_x = (t[ls(rt)].tag_x * t[rt].tag_x) % p;
        t[rs(rt)].tag_x = (t[rs(rt)].tag_x * t[rt].tag_x) % p;//乘法懒标记更新后取模 
    
        t[ls(rt)].tag = (t[ls(rt)].tag * t[rt].tag_x) % p;
        t[rs(rt)].tag = (t[rs(rt)].tag * t[rt].tag_x) % p;//加法懒标记更新 
    
        t[ls(rt)].sum = (t[ls(rt)].sum * t[rt].tag_x) % p;
        t[rs(rt)].sum = (t[rs(rt)].sum * t[rt].tag_x) % p;//sum结点对应区间和更新
    
        t[rt].tag_x = 1; //父亲的标记已经下传,就归零(因为是乘法,所以要调到1) 
    		
        t[ls(rt)].tag = (t[ls(rt)].tag + t[rt].tag) % p;
        t[rs(rt)].tag = (t[rs(rt)].tag + t[rt].tag) % p;//加法懒标记更新 
    	
        t[ls(rt)].sum += (t[ls(rt)].r - t[ls(rt)].l + 1) * t[rt].tag;
        t[rs(rt)].sum += (t[rs(rt)].r - t[rs(rt)].l + 1) * t[rt].tag;//sum结点对应区间和更新
        
        t[rt].tag = 0;//父亲的标记已经下传,就归零 
    }
    
    void change(long long rt,long long x,long long y,long long z){
    	if(x <= t[rt].l && y >= t[rt].r){
    		t[rt].tag = (t[rt].tag + z) % p;
    		t[rt].sum = (t[rt].sum + (t[rt].r - t[rt].l + 1) * z) % p; //如果修改区间覆盖了这个节点的区间,就更新 
    		return;
    	}
        if(t[rt].tag || t[rt].tag_x != 1) push_down(rt);//访问孩子结点的时候一定先把懒标记 传下去 
    	long long mid = (t[rt].l + t[rt].r) >> 1;
    	if(x <= mid){
    		change(ls(rt),x,y,z);
    	}
    	if(y > mid){
    		change(rs(rt),x,y,z);
    	}
    	//分别往左右儿子传 
    	
    	t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % p; //维护 
    }
    
    void change_x(long long rt,long long x,long long y,long long z){
    	 if(x <= t[rt].l && y >= t[rt].r){
    	 	t[rt].tag_x = (t[rt].tag_x * z) % p;
    	 	t[rt].sum = (t[rt].sum * z) % p;
    	 	t[rt].tag = (t[rt].tag * z) % p;//如果修改区间覆盖了这个节点的区间,就更新 
    	 	return;
    	 }
    	 if(t[rt].tag || t[rt].tag_x != 1) push_down(rt);//访问孩子结点的时候一定先把懒标记 传下去 
    	 long long mid = (t[rt].l + t[rt].r) >> 1;
    	 if(x <= mid){
    	 	change_x(ls(rt),x,y,z);
    	 }
    	 if(y > mid){
    	 	change_x(rs(rt),x,y,z);
    	 }
    	 //分别往左右儿子传 
    	 t[rt].sum = (t[ls(rt)].sum + t[rs(rt)].sum) % p; //维护
    }
    
    long long getsum(long long rt,long long x,long long y){
    	long long res = 0;
    	if(x <= t[rt].l && y >= t[rt].r){
    		return t[rt].sum % p;
    	}
        if(t[rt].tag || t[rt].tag_x != 1) push_down(rt);
    	long long mid = (t[rt].r + t[rt].l) >> 1;
    	if(x <= mid){
    		res += getsum(ls(rt),x,y);
    	}
    	if(y > mid){
    		res += getsum(rs(rt),x,y);
    	}
    	return res % p;
    }
    
    int main(){
    	long long i,j,x,y,z;
    	scanf("%lld%lld%lld",&n,&m,&p);
    //    scanf("%lld%lld",&n,&m); 
    	for(i = 1;i <= n; i++){
    		 scanf("%lld",&a[i]);
    	}
    	build(1,1,n);
    	for(i = 1;i <= m; i++){
    		scanf("%lld",&k);
    		
    		if(k == 1){
    			scanf("%lld%lld%lld",&x,&y,&z);
    			change_x(1,x,y,z);
    		}else if(k == 2){
    			scanf("%lld%lld%lld",&x,&y,&z);
    			change(1,x,y,z);
    		}else if(k == 3){
    			scanf("%lld%lld",&x,&y);
    			printf("%lld
    ",getsum(1,x,y));
    		}
    
    //		if(k == 1){
    //			scanf("%lld%lld%lld",&x,&y,&z);
    //			change(1,x,y,z);
    //		}else if(k == 2){
    //			scanf("%lld%lld",&x,&y);
    //			printf("%lld
    ",getsum(1,x,y));
    //		}
    		
    	}
    	return 0;
    } 
    

    指针:

    #include<cstdio>
    using namespace std;
    typedef long long int ll;
    
    const int maxn = 100005;
    int n,q,p;
    
    ll a[maxn];
    struct Node{
    	ll tag_a,v,tag_b;
    	int l,r;
    	Node *ls,*rs;
    	
    	Node(const int L,const int R)
    	{
    		l=L,r=R;
    		tag_a=0,tag_b=1;
    		if(l==r)
    		{
    			v=a[l];
    			ls=rs=NULL;
    		}
    		else
    		{
    			
    			int mid=(L+R)>>1;
    			ls=new Node(L,mid);
    			rs=new Node(mid+1,R);
    			push_up(); 
    		}
    	}
    	inline void make_tag_1(ll w)//+
    	{
    		(v+=(r-l+1)*w) %= p;
    		(tag_a+=w) %=p;
    	}
    	inline void make_tag_2(ll w)//*
    	{
    		(v*=w) %=p;
    		(tag_a *= w) %=p;
    		(tag_b*=w) %=p;
    	}
    	inline void push_up()
    	{
    		v=ls->v+rs->v;
    	}
    	inline void push_down()
    	{
    		if(!tag_a&&tag_b == 1) return;
    //		if(tag_a && tag_b != 1)
    //		{
    //			ls->make_tag_1(tag_a);
    //			rs->make_tag_1(tag_a);
    //			tag_a=0;
    //		}
    //		if(!tag_a&&tag_b)
    //		{
    //			ls->make_tag_2(tag_b);
    //			rs->make_tag_2(tag_b);
    //			tag_b=1;
    //		}
    		if(tag_b != 1){
    			ls -> make_tag_2(tag_b);
    			rs -> make_tag_2(tag_b);
    			tag_b = 1;
    		}
    		if(tag_a){
    			ls->make_tag_1(tag_a);
    			rs->make_tag_1(tag_a);
    			tag_a = 0;
    		}
    	}
    	inline bool InRange(const int L,const int R) {return (L<=l&&r<=R);}
    	inline bool Outofrange(const int L,const int R) {return (L>r||l>R);	}
    	
    	inline void update_a(const int L,const int R,ll w)//+
    	{
    		if(InRange(L,R)) make_tag_1(w);
    		else if(!Outofrange(L,R)) 
    		{
    			push_down();
    			ls->update_a(L,R,w);	
    			rs->update_a(L,R,w);
    			push_up();
    		}
    	}
    
    	inline void update_b(const int L,const int R,ll w)//*
    	{
    		if(InRange(L,R)) make_tag_2(w);
    		else if(!Outofrange(L,R))
    		{
    			push_down();
    			ls->update_b(L,R,w);
    			rs->update_b(L,R,w);
    			push_up();
    		}
    	}
    	ll query(const int L,const int R)
    	{
    		if(InRange(L,R)) { return v; }
    		if(Outofrange(L,R)){ return 0; }
    		push_down();
    		return ls->query(L,R)+rs->query(L,R);
    	}	
    };
    
    int main()
    {
    	scanf("%lld%lld%lld",&n,&q,&p);
    	for(int i=1;i<=n;i++)
    		scanf("%d",a+i);
    	Node *rot=new Node(1,n);
    	
    	for(ll o,x,y,z;q;q--)
    	{
    		scanf("%lld%lld%lld",&o,&x,&y);
    		if(o==1 )
    		{	
    			scanf("%lld",&z);
    			rot->update_b(x,y,z);
    		}
    		if(o==2)
    		{	
    			scanf("%lld",&z);
    			rot->update_a(x,y,z);
    		}
    		if(o==3)
    		{
    			ll m=rot->query(x,y);
    			printf("%lld
    ",m%p);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    python使用openpyxl操作execl
    python 修改、读取图片元数据
    python 利用pyttsx3文字转语音(转)
    SQL学习——SELECT INTO和INSERT INTO SELECT
    iOS开发环境搭建 及 编写1个hello world
    xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance
    头疼3-4次的问题,数据从DB导出到EXCEL,再从EXCEL导入到DB,数据格式发生错误 ,导致 程序出错。
    安卓自动打包持续集成
    jenkins实现master变化时,才触发构建(过滤分支)
    配置github的pull request触发jenkins自动构建
  • 原文地址:https://www.cnblogs.com/Cao-Yucong/p/13288330.html
Copyright © 2011-2022 走看看