#笔记 线段树(一)

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

部分指针用法

对于这段代码,

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;
}
原文地址:https://www.cnblogs.com/Cao-Yucong/p/13288330.html