造树计划——线段树

线段树

错误纪念

线段树原理

  • 线段树通过树上的各个节点来保存子线段的信息,比如左右端点和一些特殊的值(区间和等等)
  • 懒惰标记lazy_tag:相当于在一个节点上不断蓄势,而暂时不去考虑变化这个节点的子节点的信息。从而达到偷懒的效果。(能节省出大量的时间)

结点信息

  • 区间左右端点

  • 懒标记

  • 区间和

    struct{
       ll l,r;
       ll lazy,sum;
    }
    

操作

  • 建树build

    • 当前结点的位置、左坐标、右坐标
    • 分两种情况
      • 找到单个点
      • 找到的是区间,如果是区间的话,向左向右拆成两部分(这里是需要不断对l和r进行修改的)
    void build(ll u,ll l,ll r)
    {
        tree[u]={l,r,0,0};
        if(l==r)
        {
            tree[u]=arr[l];
        }
        ll mid = l+r>>1
        build(u<<1,l,mid);
        build(u<<1|1,mid+1,r);
        tree[u]=tree[u<<1]+tree[u<<1|1];
    }
    
  • 下放懒标记push_down(囤货的意识)

    • 两个方向的下放
      • 向左子树下放
      • 向右子树下放
    void push_down(int u)
    {
        子节点获取父节点的相关信息
        tree[u].lazy=0;//消除父节点的懒惰标记
    }
    
  • 向上更新点的和push_up

    • 注意push_up的操作是在push_down的操作后面,因为必须先解决下方的区间和,才能处理好上面根节点的和
    void push_up(int u)
    {
        tree[u]=tree[u<<1]+tree[u<<1|1];
    }
    
  • 修改区间和(加法)=区间长度*单位增量

  • 这里是不需要对l和r进行修改的,因为你随时都需要一个明确的区间,来使得树上节点所代表的区间不断加入到这个明确的区间中去

    void modify_add(ll u,ll l,ll r,ll k)
    {
    	if(tree[u].l>=l&&tree[u].r<=r)
    	{
    		tree[u].sum+=(tree[u].r-tree[u].l+1)*k;
    		tree[u].lazy_add+=k;
    	}
    	else
    	{
    		push_down(u);
    		ll mid = tree[u].l+tree[u].r>>1;
    		if(mid>=l)modify_add(u<<1,l,r,k);
    		if(mid+1<=r)modify_add(u<<1,l,r,k);
    		push_up(u);
    	}
    }
    

练习题

模板题

1模板题1传送门

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define ll long long
ll n,m,a[N];
struct Node{
	ll l,r;
	ll lazy,sum;
}tr[N<<2];

void push_up(ll u){
	tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

void build(ll u,ll l,ll r){
	tr[u]={l,r,0,0};
	if(l==r&&l==tr[u].r){
		tr[u].sum=a[l];
	}
	else {
		int mid=l+r>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		push_up(u);
	}
}
void push_down(int u){
    tr[u<<1].sum+=tr[u].lazy*(tr[u<<1].r-tr[u<<1].l+1);
	tr[u<<1].lazy+=tr[u].lazy;
	tr[u<<1|1].sum+=tr[u].lazy*(tr[u<<1|1].r-tr[u<<1|1].l+1);
	tr[u<<1|1].lazy+=tr[u].lazy;
	tr[u].lazy=0;
}
void modify(int u,int l,int r,ll k){
	if(tr[u].l>=l&&tr[u].r<=r){
		tr[u].sum+=k*(tr[u].r-tr[u].l+1);
		tr[u].lazy+=k;
	}
	else{
		int mid=(tr[u].l+tr[u].r)>>1;
		push_down(u);
		if(l<=mid)modify(u<<1,l,r,k);
		if(r>mid)modify(u<<1|1,l,r,k);
		 push_up(u);
	}
}
ll query(int u,int l,int r){
	if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
	else {
		ll res=0;
		ll mid=(tr[u].l+tr[u].r)>>1;
		push_down(u);
		if(l<=mid)res+=query(u<<1,l,r);
		if(r>mid)res+=query(u<<1|1,l,r);
		return res;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	build(1,1,n);
	while(m--){
		//for(int i=1;i<=n<<2;i++)cout<<tr[i].sum<<' ';
		ll k;
		cin>>k;
		if(k==1){
			ll l,r;
			cin>>l>>r>>k;
			modify(1,l,r,k);
		}
		else {
			ll l,r;
			cin>>l>>r;
			cout<<query(1,l,r)<<endl;
		}
	}
	
	return 0;
}


2.模板题2传送门

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1E5+10;
struct Node{
	int l,r;
	int sum,lazy_mul,lazy_add;
}tree[N];
int arr[N];
int n,m,MOD;
void push_down(ll u)
{
	tree[u<<1].sum+=(tree[u<<1].sum*tree[u].lazy_mul+(tree[u<<1].r-tree[u<<1].l+1)*tree[u].lazy_add)%MOD;
    tree[u<<1].lazy_mul*=tree[u].lazy_mul%MOD;
    tree[u<<1].lazy_add+=(tree[u<<1].lazy_add*tree[u].lazy_mul+tree[u].lazy_add)%MOD;
    
    tree[u<<1|1].sum+=(tree[u<<1|1].sum*tree[u].lazy_mul+(tree[u<<1|1].r-tree[u<<1|1].l+1)*tree[u].lazy_add)%MOD;
    tree[u<<1|1].lazy_mul*=tree[u].lazy_mul;
    tree[u<<1|1].lazy_add+=tree[u<<1|1].lazy_add*tree[u].lazy_mul+tree[u].lazy_add;
    
    tree[u].lazy_mul=1;
    tree[u].lazy_add=0;
}
void push_up(ll u)
{
	tree[u].sum+=(tree[u<<1].sum+tree[u<<1|1].sum)%MOD;
}
void build_tree(ll u , ll l ,ll r)
{
	tree[u]={l,r,0,1,0};
	if(l==r)
	{
		tree[u].sum=arr[l];
	}
	else
	{
		ll mid = l+r>>1;
		build_tree(u<<1,l,mid);
		build_tree(u<<1|1,mid+1,r);
		push_up(u);
	}
}
void modify_add(ll u,ll l,ll r,ll k)
{
	if(tree[u].l>=l&&tree[u].r<=r)
	{
		tree[u].sum+=(tree[u].r-tree[u].l+1)*k;
		tree[u].lazy_add+=k;
	}
	else
	{
		push_down(u);
		ll mid = tree[u].l+tree[u].r>>1;
		if(mid>=l)modify_add(u<<1,l,mid,k);
		if(mid+1<=r)modify_add(u<<1,mid+1,r,k);
		push_up(u);
	}
}
void modify_mul(ll u,ll l,ll r,ll k)
{
	if(tree[u].l>=l&&tree[u].r<=r)
	{
		tree[u].sum+=tree[u].sum*k;
		tree[u].lazy_add*=k;
		tree[u].lazy_mul*=k;
	}
	else
	{
		push_down(u);
		ll mid = tree[u].l+tree[u].r>>1;
		if(mid>=l)modify_mul(u<<1,l,mid,k);
		if(mid+1<=r)modify_mul(u<<1|1,mid+1,r,k);
		push_up(u);
	}
}
ll query(ll u, ll l ,ll r)
{
	if(tree[u].l>=l&&tree[u].r<=r)
	{
		return tree[u].sum;
	}
	else
	{
		ll res = 0;
		ll mid = tree[u].l+tree[u].r>>1;
		push_down(u);//没有办法整段加和,必须下放 
		if(l<=mid)res+=query(u<<1,l,r);//有必要搜索 
		if(mid<r)res+=query(u<<1|1,l,r); //有必要搜索
		return res; 
	}
}
int main()
{
	cin>>n>>m>>MOD;
	for(int i = 1 ;i<=n;i++)
	{
		cin>>arr[i];
	}
	build_tree(1,1,n);
	for(int i = 1;i<=m;i++)
	{
		int oper;
		cin>>oper;
		int x,y,k;
		if(oper==1)
		{
			cin>>x>>y>>k;
			modify_add(1,x,y,k);
		}
		else if(oper==2)
		{
			cin>>x>>y>>k;
			modify_mul(1,x,y,k);
		}
		else if(oper==3)
		{
			cin>>x>>y;
			cout<<query(1,x,y);
		}
	}
	return 0;
}

一般的题目

P2574 XOR的艺术

#include<bits/stdc++.h>
using namespace std;
const int N = 2*1e5+10;
#define ll long long
string s;
ll n,m;
struct NODE{
	ll l,r;
	ll sum_0,sum_1,lazy_change;
}tree[N<<2];
void resverse(ll u)
{
	ll t=tree[u].sum_1;
	tree[u].sum_1=tree[u].sum_0;
	tree[u].sum_0=t;
	
}
void push_down(ll u)
{
	if(tree[u].lazy_change==1)
	{
		resverse(u<<1);resverse(u<<1|1);
		tree[u<<1].lazy_change=(tree[u<<1].lazy_change+tree[u].lazy_change)%2;
		tree[u<<1|1].lazy_change=(tree[u<<1|1].lazy_change+tree[u].lazy_change)%2;
	}
	tree[u].lazy_change=0;
}
void push_up(ll u)
{
	tree[u].sum_0=tree[u<<1].sum_0+tree[u<<1|1].sum_0;
	tree[u].sum_1=tree[u<<1].sum_1+tree[u<<1|1].sum_1;
}
void build_tree(ll u,ll l,ll r)
{
	tree[u]={l,r,0,0,0};
	if(l==r)
	{
		if(s[l-1]=='0')
		{
			tree[u].sum_0++;
		}
		else if(s[l-1]=='1') 
		{
			tree[u].sum_1++;
		}
	}
	else
	{
		ll mid=l+r>>1;
		build_tree(u<<1,l,mid);
		build_tree(u<<1|1,mid+1,r);
		push_up(u);
	}
}
void modify_resverse(ll u,ll l,ll r)
{
	if(tree[u].l>=l&&tree[u].r<=r)
	{
	    resverse(u);//直接反转 
		tree[u].lazy_change=tree[u].lazy_change^1;		
	}
	else
	{
		ll mid = tree[u].l+tree[u].r>>1;
		push_down(u);
		if(mid>=l)modify_resverse(u<<1,l,r);
		if(mid+1<=r)modify_resverse(u<<1|1,l,r);
		push_up(u);	
	}
}
ll query(ll u,ll l,ll r)
{
	if(tree[u].l>=l&&tree[u].r<=r)
	{
		return tree[u].sum_1;
	}
	else
	{
		push_down(u);
		ll mid=tree[u].l+tree[u].r>>1;
		ll res=0;
		if(mid>=l)res+=query(u<<1,l,r);
		if(mid+1<=r)res+=query(u<<1|1,l,r);
		push_up(u);
		return res;
	}
}
int main()
{
	cin>>n>>m>>s;
    build_tree(1,1,n);
	
    int oper,x,y;
    for(int i=1;i<=m;i++)
    {
    	cin>>oper;
    	if(oper==0)
    	{
    		cin>>x>>y;
    		modify_resverse(1,x,y);
		}
		else if(oper==1)
		{
			cin>>x>>y;
			cout<<query(1,x,y)<<endl;
		}
	}
    return 0;	
} 

感谢

感谢黄老师的模板和教导,感谢ztdl,jldl。

原文地址:https://www.cnblogs.com/BeautifulWater/p/14725094.html